diff options
Diffstat (limited to 'client/screen.cpp')
-rw-r--r-- | client/screen.cpp | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/client/screen.cpp b/client/screen.cpp new file mode 100644 index 00000000..b33560ee --- /dev/null +++ b/client/screen.cpp @@ -0,0 +1,777 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "screen.h" +#include "application.h" +#include "screen_layer.h" +#include "utils.h" +#include "debug.h" +#include "monitor.h" +#include "red_pixmap_cairo.h" +#include "resource.h" +#include "icon.h" + +class UpdateEvent: public Event { +public: + UpdateEvent(int screen) : _screen (screen) {} + + virtual void responce(Application& application) + { + RedScreen* screen = application.find_screen(_screen); + if (screen) { + screen->update(); + } + } + +private: + int _screen; +}; + +void periodic_update_proc(void *opaque, TimerID timer) +{ + RedScreen* screen = (RedScreen*)opaque; + + screen->periodic_update(); +} + +RedScreen::RedScreen(Application& owner, int id, const std::wstring& name, int width, int height) + : _owner (owner) + , _id (id) + , _refs (1) + , _window (*this) + , _active (false) + , _captured (false) + , _full_screen (false) + , _out_of_sync (false) + , _frame_area (false) + , _cursor_visible (true) + , _periodic_update (false) + , _key_interception (false) + , _update_by_timer (true) + , _forec_update_timer (0) + , _update_timer (INVALID_TIMER) + , _composit_area (NULL) + , _update_mark (1) + , _monitor (NULL) + , _default_cursor (NULL) + , _active_cursor (NULL) + , _inactive_cursor (NULL) + , _pointer_location (POINTER_OUTSIDE_WINDOW) + , _pixel_format_index (0) + , _update_interrupt_trigger (NULL) +{ + region_init(&_dirty_region); + set_name(name); + _size.x = width; + _size.y = height; + _origin.x = _origin.y = 0; + create_composit_area(); + _window.resize(_size.x, _size.y); + save_position(); + _update_timer = Platform::create_interval_timer(periodic_update_proc, this); + if (_update_timer == INVALID_TIMER) { + THROW("create timer failed"); + } + if ((_default_cursor = Platform::create_default_cursor()) == NULL) { + THROW("create default cursor failed"); + } + if ((_inactive_cursor = Platform::create_inactive_cursor()) == NULL) { + THROW("create inactive cursor failed"); + } + _window.set_cursor(_default_cursor); + AutoRef<Menu> menu(_owner.get_app_menu()); + _window.set_menu(*menu); + AutoRef<Icon> icon(Platform::load_icon(RED_ICON_RES_ID)); + _window.set_icon(*icon); +} + +RedScreen::~RedScreen() +{ + bool captured = is_captured(); + relase_inputs(); + destroy_composit_area(); + Platform::destroy_interval_timer(_update_timer); + _owner.on_screen_destroyed(_id, captured); + region_destroy(&_dirty_region); + if (_default_cursor) { + _default_cursor->unref(); + } + if (_active_cursor) { + _active_cursor->unref(); + } + if (_inactive_cursor) { + _inactive_cursor->unref(); + } +} + +void RedScreen::show(bool activate, RedScreen* pos) +{ + _window.position_after((pos) ? &pos->_window : NULL); + show(); + if (activate) { + _window.activate(); + } +} + +RedScreen* RedScreen::ref() +{ + _refs++; + return this; +} + +void RedScreen::unref() +{ + if (!--_refs) { + delete this; + } +} + +void RedScreen::destroy_composit_area() +{ + if (_composit_area) { + delete _composit_area; + _composit_area = NULL; + } +} + +void RedScreen::create_composit_area() +{ + destroy_composit_area(); + _composit_area = new RedPixmapCairo(_size.x, _size.y, RedPixmap::RGB32, + false, NULL, NULL); +} + +void RedScreen::adjust_window_rect(int x, int y) +{ + _window.move_and_resize(x, y, _size.x, _size.y); +} + +void RedScreen::set_mode(int width, int height, int depth) +{ + RecurciveLock lock(_update_lock); + _size.x = width; + _size.y = height; + create_composit_area(); + if (_full_screen) { + bool cuptur = _owner.rearrange_monitors(*this); + __show_full_screen(); + if (cuptur) { + capture_inputs(); + } + } else { + _window.resize(_size.x, _size.y); + } + notify_new_size(); +} + +void RedScreen::set_name(const std::wstring& name) +{ + if (!name.empty()) { + wstring_printf(_name, name.c_str(), _id); + } + _window.set_title(_name); +} + +void RedScreen::attach_layer(ScreenLayer& layer) +{ + RecurciveLock lock(_update_lock); + int order = layer.z_order(); + + if ((int)_layes.size() < order + 1) { + _layes.resize(order + 1); + } + if (_layes[order]) { + THROW("layer in use"); + } + layer.set_screen(this); + _layes[order] = &layer; + ref(); + lock.unlock(); + layer.invalidate(); +} + +void RedScreen::detach_layer(ScreenLayer& layer) +{ + RecurciveLock lock(_update_lock); + int order = layer.z_order(); + + if ((int)_layes.size() < order + 1 || _layes[order] != &layer) { + THROW("not found"); + } + QRegion layer_area; + region_clone(&layer_area, &layer.area()); + _layes[order]->set_screen(NULL); + _layes[order] = NULL; + lock.unlock(); + invalidate(layer_area); + region_destroy(&layer_area); + unref(); +} + +void RedScreen::composit_to_screen(RedDrawable& win_dc, const QRegion& region) +{ + for (int i = 0; i < (int)region.num_rects; i++) { + Rect* r = ®ion.rects[i]; + win_dc.copy_pixels(*_composit_area, r->left, r->top, *r); + } +} + +void RedScreen::notify_new_size() +{ + for (int i = 0; i < (int)_layes.size(); i++) { + if (!_layes[i]) { + continue; + } + _layes[i]->on_size_changed(); + } +} + +inline void RedScreen::begin_update(QRegion& direct_rgn, QRegion& composit_rgn, + QRegion& frame_rgn) +{ + region_init(&composit_rgn); + RecurciveLock lock(_update_lock); + region_clone(&direct_rgn, &_dirty_region); + region_clear(&_dirty_region); + _update_mark++; + lock.unlock(); + + QRegion rect_rgn; + Rect r; + r.top = r.left = 0; + r.right = _size.x; + r.bottom = _size.y; + region_init(&rect_rgn); + region_add(&rect_rgn, &r); + + if (_frame_area) { + region_clone(&frame_rgn, &direct_rgn); + region_exclude(&frame_rgn, &rect_rgn); + } + region_and(&direct_rgn, &rect_rgn); + region_destroy(&rect_rgn); + + for (int i = _layes.size() - 1; i >= 0; i--) { + ScreenLayer* layer; + + if (!(layer = _layes[i])) { + continue; + } + layer->begin_update(direct_rgn, composit_rgn); + } +} + +inline void RedScreen::update_done() +{ + for (unsigned int i = 0; i < _layes.size(); i++) { + ScreenLayer* layer; + + if (!(layer = _layes[i])) { + continue; + } + layer->on_update_completion(_update_mark - 1); + } +} + +inline void RedScreen::update_composit(QRegion& composit_rgn) +{ + erase_background(*_composit_area, composit_rgn); + for (int i = 0; i < (int)_layes.size(); i++) { + ScreenLayer* layer; + + if (!(layer = _layes[i])) { + continue; + } + QRegion& dest_region = layer->composit_area(); + region_or(&composit_rgn, &dest_region); + layer->copy_pixels(dest_region, *_composit_area); + } +} + +inline void RedScreen::draw_direct(RedDrawable& win_dc, QRegion& direct_rgn, QRegion& composit_rgn, + QRegion& frame_rgn) +{ + erase_background(win_dc, direct_rgn); + + if (_frame_area) { + erase_background(win_dc, frame_rgn); + region_destroy(&frame_rgn); + } + + for (int i = 0; i < (int)_layes.size(); i++) { + ScreenLayer* layer; + + if (!(layer = _layes[i])) { + continue; + } + QRegion& dest_region = layer->direct_area(); + layer->copy_pixels(dest_region, win_dc); + } +} + +void RedScreen::periodic_update() +{ + bool need_update; + RecurciveLock lock(_update_lock); + if (is_dirty()) { + need_update = true; + } else { + if (!_forec_update_timer) { + Platform::deactivate_interval_timer(_update_timer); + _periodic_update = false; + } + need_update = false; + } + lock.unlock(); + + if (need_update) { + if (update_by_interrupt()) { + interrupt_update(); + } else { + update(); + } + } +} + +void RedScreen::activate_timer() +{ + RecurciveLock lock(_update_lock); + if (_periodic_update) { + return; + } + _periodic_update = true; + lock.unlock(); + if (!Platform::activate_interval_timer(_update_timer, 1000 / 30)) { + LOG_WARN("failed"); + } +} + +void RedScreen::update() +{ + if (is_out_of_sync()) { + return; + } + + QRegion direct_rgn; + QRegion composit_rgn; + QRegion frame_rgn; + + begin_update(direct_rgn, composit_rgn, frame_rgn); + update_composit(composit_rgn); + draw_direct(_window, direct_rgn, composit_rgn, frame_rgn); + composit_to_screen(_window, composit_rgn); + update_done(); + region_destroy(&direct_rgn); + region_destroy(&composit_rgn); + + if (_update_by_timer) { + activate_timer(); + } +} + +bool RedScreen::_invalidate(const Rect& rect, bool urgent, uint64_t& update_mark) +{ + RecurciveLock lock(_update_lock); + bool update_triger = !is_dirty() && (urgent || !_periodic_update); + region_add(&_dirty_region, &rect); + update_mark = _update_mark; + return update_triger; +} + +uint64_t RedScreen::invalidate(const Rect& rect, bool urgent) +{ + uint64_t update_mark; + if (_invalidate(rect, urgent, update_mark)) { + if (!urgent && _update_by_timer) { + activate_timer(); + } else { + if (update_by_interrupt()) { + interrupt_update(); + } else { + AutoRef<UpdateEvent> update_event(new UpdateEvent(_id)); + _owner.push_event(*update_event); + } + } + } + return update_mark; +} + +void RedScreen::invalidate(const QRegion ®ion) +{ + Rect *r = region.rects; + Rect *end = r + region.num_rects; + while (r != end) { + invalidate(*r++, false); + } +} + +inline void RedScreen::erase_background(RedDrawable& dc, const QRegion& composit_rgn) +{ + for (int i = 0; i < (int)composit_rgn.num_rects; i++) { + dc.fill_rect(composit_rgn.rects[i], 0); + } +} + +void RedScreen::reset_mouse_pos() +{ + _window.set_mouse_position(_mouse_anchor_point.x, _mouse_anchor_point.y); +} + +void RedScreen::capture_inputs() +{ + if (_captured || !_window.get_mouse_anchor_point(_mouse_anchor_point)) { + return; + } + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) { + _window.hide_cursor(); + reset_mouse_pos(); + _window.cupture_mouse(); + } +#ifndef NO_KEY_GRAB + _window.start_key_interception(); +#endif + _captured = true; +} + +void RedScreen::relase_inputs() +{ + if (!_captured) { + return; + } +#ifndef NO_KEY_GRAB + _window.stop_key_interception(); +#endif + _captured = false; + _window.release_mouse(); + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) { + _window.set_cursor(_default_cursor); + } +} + +void RedScreen::set_cursor(CursorData* cursor) +{ + if (cursor) { + if (_active_cursor) { + _active_cursor->unref(); + } + if (!cursor->get_local()) { + AutoRef<LocalCursor> cur(Platform::create_local_cursor(cursor)); + if (*cur == NULL) { + THROW("create local cursor failed"); + } + cursor->set_local(*cur); + _active_cursor = (*cur)->ref(); + } else { + _active_cursor = (cursor->get_local())->ref(); + } + } + _cursor_visible = !!cursor; + update_active_cursor(); +} + +void RedScreen::update_active_cursor() +{ + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_CLIENT && + _pointer_location == POINTER_IN_ACTIVE_AREA) { + if (_cursor_visible && _active_cursor) { + _window.set_cursor(_active_cursor); + } else { + _window.hide_cursor(); + } + } +} + +void RedScreen::on_mouse_motion(int x, int y, unsigned int buttons_state) +{ + switch (_owner.get_mouse_mode()) { + case RED_MOUSE_MODE_CLIENT: + if (!_frame_area) { + _owner.on_mouse_position(x, y, buttons_state, _id); + } else if (x >= 0 && x < _size.x && y >= 0 && y < _size.y) { + _owner.on_mouse_position(x, y, buttons_state, _id); + if (_pointer_location != POINTER_IN_ACTIVE_AREA) { + _pointer_location = POINTER_IN_ACTIVE_AREA; + update_active_cursor(); + } + } else if (_pointer_location != POINTER_IN_FRAME_AREA) { + _pointer_location = POINTER_IN_FRAME_AREA; + _window.set_cursor(_inactive_cursor); + } + break; + case RED_MOUSE_MODE_SERVER: + if (_captured && (x != _mouse_anchor_point.x || y != _mouse_anchor_point.y)) { + _owner.on_mouse_motion(x - _mouse_anchor_point.x, + y - _mouse_anchor_point.y, + buttons_state); + reset_mouse_pos(); + } + break; + default: + THROW("invalid mouse mode"); + } +} + +void RedScreen::on_button_press(RedButton button, unsigned int buttons_state) +{ + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_CLIENT && + _pointer_location != POINTER_IN_ACTIVE_AREA) { + return; + } + if (!mouse_is_captured()) { + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER && button != REDC_MOUSE_LBUTTON) { + return; + } + capture_inputs(); + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) { + return; + } + } + _owner.on_mouse_down(button, buttons_state); +} + +void RedScreen::on_button_release(RedButton button, unsigned int buttons_state) +{ + if (!mouse_is_captured()) { + if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) { + return; + } + capture_inputs(); + } + _owner.on_mouse_up(button, buttons_state); +} + +void RedScreen::on_key_press(RedKey key) +{ + _owner.on_key_down(key); +} + +void RedScreen::on_key_release(RedKey key) +{ + _owner.on_key_up(key); +} + +void RedScreen::on_deactivate() +{ + relase_inputs(); + _active = false; + _owner.on_deactivate_screen(this); +} + +void RedScreen::on_activate() +{ + _active = true; + _owner.on_activate_screen(this); +} + +void RedScreen::on_pointer_enter() +{ + if (!_frame_area) { + _pointer_location = POINTER_IN_ACTIVE_AREA; + update_active_cursor(); + } +} + +void RedScreen::on_pointer_leave() +{ + _pointer_location = POINTER_OUTSIDE_WINDOW; +} + +void RedScreen::enter_modal_loop() +{ + _forec_update_timer++; + activate_timer(); +} + +void RedScreen::exit_modal_loop() +{ + ASSERT(_forec_update_timer > 0) + _forec_update_timer--; +} + +void RedScreen::pre_migrate() +{ + for (int i = 0; i < (int)_layes.size(); i++) { + if (!_layes[i]) { + continue; + } + _layes[i]->pre_migrate(); + } +} + +void RedScreen::post_migrate() +{ + for (int i = 0; i < (int)_layes.size(); i++) { + if (!_layes[i]) { + continue; + } + _layes[i]->post_migrate(); + } +} + +void RedScreen::exit_full_screen() +{ + if (!_full_screen) { + return; + } + RecurciveLock lock(_update_lock); + _window.hide(); + region_clear(&_dirty_region); + _window.set_type(RedWindow::TYPE_NORMAL); + adjust_window_rect(_save_pos.x, _save_pos.y); + _origin.x = _origin.y = 0; + _window.set_origin(0, 0); + show(); + _full_screen = false; + _out_of_sync = false; + _frame_area = false; +} + +void RedScreen::save_position() +{ + _save_pos = _window.get_position(); +} + +void RedScreen::__show_full_screen() +{ + if (!_monitor) { + hide(); + return; + } + Point position = _monitor->get_position(); + Point monitor_size = _monitor->get_size(); + _frame_area = false; + region_clear(&_dirty_region); + _window.set_type(RedWindow::TYPE_FULLSCREEN); + _window.move_and_resize(position.x, position.y, monitor_size.x, monitor_size.y); + + if (!(_out_of_sync = _monitor->is_out_of_sync())) { + ASSERT(monitor_size.x >= _size.x); + ASSERT(monitor_size.y >= _size.y); + _origin.x = (monitor_size.x - _size.x) / 2; + _origin.y = (monitor_size.y - _size.y) / 2; + _frame_area = monitor_size.x != _size.x || monitor_size.y != _size.y; + } else { + _origin.x = _origin.y = 0; + } + _window.set_origin(_origin.x, _origin.y); + show(); +} + +void RedScreen::show_full_screen() +{ + if (_full_screen) { + return; + } + RecurciveLock lock(_update_lock); + hide(); + save_position(); + _full_screen = true; + __show_full_screen(); +} + +void RedScreen::minimize() +{ + _window.minimize(); +} + +void RedScreen::position_full_screen(const Point& position) +{ + if (!_full_screen) { + return; + } + + _window.move(position.x, position.y); +} + +void RedScreen::hide() +{ + _window.hide(); +} + +void RedScreen::show() +{ + RecurciveLock lock(_update_lock); + _window.show(_monitor ? _monitor->get_screen_id() : 0); +} + +void RedScreen::activate() +{ + _window.activate(); +} + +void RedScreen::external_show() +{ + DBG(0, "Entry"); + _window.external_show(); +} + +void RedScreen::on_exposed_rect(const Rect& area) +{ + if (is_out_of_sync()) { + _window.fill_rect(area, rgb32_make(0xff, 0xff, 0xff)); + return; + } + invalidate(area, false); +} + +int RedScreen::get_screen_id() +{ + return _monitor ? _monitor->get_screen_id() : 0; +} + +#ifdef USE_OGL +void RedScreen::untouch_context() +{ + _window.untouch_context(); +} + +bool RedScreen::need_recreate_context_gl() +{ + if (_full_screen) { + return true; + } + return false; +} + +#endif + +void RedScreen::set_update_interrupt_trigger(EventsLoop::Trigger *trigger) +{ + _update_interrupt_trigger = trigger; +} + +bool RedScreen::update_by_interrupt() +{ + return _update_interrupt_trigger != NULL; +} + +void RedScreen::interrupt_update() +{ + _update_interrupt_trigger->trigger(); +} + +void RedScreen::set_type_gl() +{ + _window.set_type_gl(); +} + +void RedScreen::unset_type_gl() +{ + _window.unset_type_gl(); +} + |