summaryrefslogtreecommitdiffstats
path: root/client/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/screen.cpp')
-rw-r--r--client/screen.cpp777
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 = &region.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 &region)
+{
+ 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();
+}
+