summaryrefslogtreecommitdiffstats
path: root/client/application.cpp
diff options
context:
space:
mode:
authorYonit Halperin <yhalperi@redhat.com>2009-11-12 19:57:34 +0200
committerYaniv Kamay <ykamay@redhat.com>2009-11-15 13:41:10 +0200
commita461f0655f1d97503fe854d285da95c306d7edf8 (patch)
treeca7a0b9376209c2198cb1cd9e6cedcf90a41270f /client/application.cpp
parent39153191258c4a37e4e61241c3149f7ca08e217c (diff)
downloadspice-a461f0655f1d97503fe854d285da95c306d7edf8.tar.gz
spice-a461f0655f1d97503fe854d285da95c306d7edf8.tar.xz
spice-a461f0655f1d97503fe854d285da95c306d7edf8.zip
spice client: sticky Alt activation when holding an Alt key: bug #505912.
Additional changes that were required for the feature: 1) focusing on the pointed window in full screen mode 2) In X11 - handling events that occur during keyboard ungrabbing 3) In X11 - handling Leave/Enter Notify events that occur during keyboard grabbing/ungrabbing 4) In X11 - fix for focus events that are handled in the wrong order (happens when focus events occur during grabbing the keyboard) 5) In X11 - ignoring key release events during key holding 6) In Windows - synchronizing keyboard release events that occured during a modal loop
Diffstat (limited to 'client/application.cpp')
-rw-r--r--client/application.cpp233
1 files changed, 216 insertions, 17 deletions
diff --git a/client/application.cpp b/client/application.cpp
index 3b797539..5a60b9a9 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -41,11 +41,15 @@
#include "mutex.h"
#include "cmd_line_parser.h"
#include "tunnel_channel.h"
+#include "rect.h"
#include <log4cpp/BasicConfigurator.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/RollingFileAppender.hh>
+#define STICKY_KEY_PIXMAP ALT_IMAGE_RES_ID
+#define STICKY_KEY_TIMEOUT 750
+
#ifdef CAIRO_CANVAS_CACH_IS_SHARED
mutex_t cairo_surface_user_data_mutex;
#endif
@@ -95,6 +99,7 @@ public:
void set_splash_mode();
void set_info_mode();
+ void set_sticky(bool is_on);
virtual void on_size_changed();
private:
@@ -104,22 +109,29 @@ private:
private:
ImageFromRes _splash_pixmap;
AlphaImageFromRes _info_pixmap;
+ AlphaImageFromRes _sticky_pixmap;
Point _splash_pos;
Point _info_pos;
+ Point _sticky_pos;
+ Rect _sticky_rect;
bool _splash_mode;
- Mutex _update_lock;
+ bool _sticky_on;
+ RecurciveMutex _update_lock;
};
GUILayer::GUILayer()
: ScreenLayer(SCREEN_LAYER_GUI, false)
, _splash_pixmap (SPLASH_IMAGE_RES_ID)
, _info_pixmap (INFO_IMAGE_RES_ID)
+ , _sticky_pixmap (STICKY_KEY_PIXMAP)
, _splash_mode (false)
+ , _sticky_on (false)
{
}
void GUILayer::draw_splash(const QRegion& dest_region, RedDrawable& dest)
{
+ ASSERT(!_sticky_on);
for (int i = 0; i < (int)dest_region.num_rects; i++) {
Rect* r = &dest_region.rects[i];
dest.copy_pixels(_splash_pixmap, r->left - _splash_pos.x, r->top - _splash_pos.y, *r);
@@ -130,13 +142,18 @@ void GUILayer::draw_info(const QRegion& dest_region, RedDrawable& dest)
{
for (int i = 0; i < (int)dest_region.num_rects; i++) {
Rect* r = &dest_region.rects[i];
- dest.blend_pixels(_info_pixmap, r->left - _info_pos.x, r->top - _info_pos.y, *r);
+ /* is rect inside sticky region or info region? */
+ if (_sticky_on && rect_intersects(*r, _sticky_rect)) {
+ dest.blend_pixels(_sticky_pixmap, r->left - _sticky_pos.x, r->top - _sticky_pos.y, *r);
+ } else {
+ dest.blend_pixels(_info_pixmap, r->left - _info_pos.x, r->top - _info_pos.y, *r);
+ }
}
}
void GUILayer::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
{
- Lock lock(_update_lock);
+ RecurciveLock lock(_update_lock);
if (_splash_mode) {
draw_splash(dest_region, dest_dc);
} else {
@@ -146,7 +163,7 @@ void GUILayer::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
void GUILayer::set_splash_mode()
{
- Lock lock(_update_lock);
+ RecurciveLock lock(_update_lock);
Point size = _splash_pixmap.get_size();
Point screen_size = screen()->get_size();
Rect r;
@@ -158,11 +175,12 @@ void GUILayer::set_splash_mode()
_splash_mode = true;
lock.unlock();
set_rect_area(r);
+ ASSERT(!_sticky_on);
}
void GUILayer::set_info_mode()
{
- Lock lock(_update_lock);
+ RecurciveLock lock(_update_lock);
Point size = _info_pixmap.get_size();
Point screen_size = screen()->get_size();
Rect r;
@@ -175,6 +193,33 @@ void GUILayer::set_info_mode()
_splash_mode = false;
lock.unlock();
set_rect_area(r);
+
+ set_sticky(_sticky_on);
+}
+
+void GUILayer::set_sticky(bool is_on)
+{
+ RecurciveLock lock(_update_lock);
+ if (!_sticky_on && !is_on) {
+ return;
+ }
+
+ Point size = _sticky_pixmap.get_size();
+ Point screen_size = screen()->get_size();
+
+ _sticky_on = is_on;
+ if (_sticky_on) {
+ _sticky_pos.x = (screen_size.x - size.x) / 2;
+ _sticky_pos.y = screen_size.y * 2 / 3;
+ _sticky_rect.left = _sticky_pos.x;
+ _sticky_rect.top = _sticky_pos.y;
+ _sticky_rect.right = _sticky_rect.left + size.x;
+ _sticky_rect.bottom = _sticky_rect.top + size.y;
+ add_rect_area(_sticky_rect);
+ invalidate();
+ } else {
+ remove_rect_area(_sticky_rect);
+ }
}
void GUILayer::on_size_changed()
@@ -182,6 +227,20 @@ void GUILayer::on_size_changed()
set_info_mode();
}
+void StickyKeyTimer::response(AbstractProcessLoop& events_loop)
+{
+ Application* app = (Application*)events_loop.get_owner();
+ StickyInfo* sticky_info = &app->_sticky_info;
+ ASSERT(app->is_sticky_trace_key(sticky_info->key));
+ ASSERT(app->_key_table[sticky_info->key ].press);
+ ASSERT(sticky_info->key_first_down);
+ ASSERT(sticky_info->key_down);
+ sticky_info->sticky_mode = true;
+ DBG(0, "ON sticky");
+ app->_gui_layer->set_sticky(true);
+ app->deactivate_interval_timer(this);
+}
+
static InputsHandler default_inputs_handler;
enum AppCommands {
@@ -212,6 +271,8 @@ Application::Application()
, _inputs_handler (&default_inputs_handler)
, _monitors (NULL)
, _title (L"SPICEc:%d")
+ , _splash_mode (true)
+ , _sys_key_intercept_mode (false)
{
DBG(0, "");
Platform::set_process_loop(*this);
@@ -248,6 +309,13 @@ Application::Application()
#endif
, _commands_map));
_hot_keys = parser->get();
+
+ _sticky_info.trace_is_on = false;
+ _sticky_info.sticky_mode = false;
+ _sticky_info.key_first_down = false;
+ _sticky_info.key_down = false;
+ _sticky_info.key = REDKEY_INVALID;
+ _sticky_info.timer.reset(new StickyKeyTimer());
}
Application::~Application()
@@ -473,6 +541,7 @@ void Application::init_pause_scan_code()
void Application::init_key_table()
{
memset(_key_table, 0, sizeof(_key_table));
+ _num_keys_pressed = 0;
init_scan_code(REDKEY_ESCAPE);
init_scan_code(REDKEY_1);
init_scan_code(REDKEY_2);
@@ -608,12 +677,13 @@ inline uint32_t Application::get_break_scan_code(RedKey key)
void Application::unpress_all()
{
+ reset_sticky();
for (int i = 0; i < REDKEY_NUM_KEYS; i++) {
if (_key_table[i].press) {
uint32_t scan_code = get_break_scan_code((RedKey)i);
ASSERT(scan_code);
_inputs_handler->on_key_up(scan_code);
- _key_table[i].press = false;
+ unpress_key((RedKey)i);
}
}
}
@@ -792,6 +862,57 @@ static void show_red_key(RedKey key)
#endif
+bool Application::press_key(RedKey key)
+{
+ if (_key_table[key].press) {
+ return true;
+ } else {
+ _key_table[key].press = true;
+ _num_keys_pressed++;
+ return false;
+ }
+}
+
+bool Application::unpress_key(RedKey key)
+{
+ ASSERT(!_sticky_info.key_down || !is_sticky_trace_key(key));
+
+ if (_key_table[key].press) {
+ _key_table[key].press = false;
+ _num_keys_pressed--;
+ ASSERT(_num_keys_pressed >= 0);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+inline bool Application::is_sticky_trace_key(RedKey key)
+{
+ return ((key == REDKEY_L_ALT) || (key == REDKEY_R_ALT));
+}
+
+void Application::reset_sticky()
+{
+ _sticky_info.trace_is_on = !_splash_mode && _sys_key_intercept_mode;
+ _sticky_info.key_first_down = false;
+ deactivate_interval_timer(*_sticky_info.timer);
+ if (_sticky_info.sticky_mode) {
+ ASSERT(_key_table[_sticky_info.key].press);
+ // if it is physically down, we shouldn't unpress it
+ if (!_sticky_info.key_down) {
+ do_on_key_up(_sticky_info.key);
+ }
+ _sticky_info.sticky_mode = false;
+ DBG(0, "OFF sticky");
+ _gui_layer->set_sticky(false);
+ }
+ _sticky_info.key_down = false;
+
+ _sticky_info.key = REDKEY_INVALID;
+
+}
+
void Application::on_key_down(RedKey key)
{
if (key <= 0 || key >= REDKEY_NUM_KEYS) {
@@ -803,7 +924,30 @@ void Application::on_key_down(RedKey key)
LOG_WARN("no make code for %d", key);
return;
}
- _key_table[key].press = true;
+
+ bool was_pressed = press_key(key);
+ if (_sticky_info.trace_is_on) {
+ if (key == _sticky_info.key) {
+ _sticky_info.key_down = true;
+ }
+
+ if (!_sticky_info.sticky_mode) {
+ // during tracing (traced key was pressed and no keyboard event has occured till now)
+ if (_sticky_info.key_first_down) {
+ ASSERT(_sticky_info.key != REDKEY_INVALID);
+ if (key != _sticky_info.key) {
+ reset_sticky();
+ }
+ } else if (is_sticky_trace_key(key) && (_num_keys_pressed == 1) && !was_pressed) {
+ ASSERT(_sticky_info.key == REDKEY_INVALID);
+ // start tracing
+ _sticky_info.key = key;
+ _sticky_info.key_first_down = true;
+ _sticky_info.key_down = true;
+ activate_interval_timer(*_sticky_info.timer, STICKY_KEY_TIMEOUT);
+ }
+ }
+ }
int command = get_hotkeys_commnad();
if (command != APP_CMD_INVALID) {
@@ -815,17 +959,18 @@ void Application::on_key_down(RedKey key)
if (!_active_screen->intercepts_sys_key() &&
(key == REDKEY_LEFT_CMD || key == REDKEY_RIGHT_CMD ||
key == REDKEY_MENU || _key_table[REDKEY_L_ALT].press)) {
- _key_table[key].press = false;
+ unpress_key(key);
return;
}
- if ((_key_table[REDKEY_L_CTRL].press || _key_table[REDKEY_R_CTRL].press) &&
- (_key_table[REDKEY_L_ALT].press || _key_table[REDKEY_R_ALT].press)) {
+ if (!_sticky_info.sticky_mode &&
+ ((_key_table[REDKEY_L_CTRL].press || _key_table[REDKEY_R_CTRL].press) &&
+ (_key_table[REDKEY_L_ALT].press || _key_table[REDKEY_R_ALT].press))) {
if (key == REDKEY_END || key == REDKEY_PAD_1) {
- _key_table[key].press = false;
+ unpress_key(key);
_inputs_handler->on_key_down(get_make_scan_code(REDKEY_DELETE));
_inputs_handler->on_key_up(get_break_scan_code(REDKEY_DELETE));
} else if (key == REDKEY_DELETE || key == REDKEY_PAD_POINT) {
- _key_table[key].press = false;
+ unpress_key(key);
return;
}
}
@@ -834,12 +979,9 @@ void Application::on_key_down(RedKey key)
_inputs_handler->on_key_down(scan_code);
}
-void Application::on_key_up(RedKey key)
+void Application::do_on_key_up(RedKey key)
{
- if (key < 0 || key >= REDKEY_NUM_KEYS || !_key_table[key].press) {
- return;
- }
- _key_table[key].press = false;
+ unpress_key(key);
uint32_t scan_code = get_break_scan_code(key);
if (!scan_code) {
LOG_WARN("no break code for %d", key);
@@ -848,9 +990,43 @@ void Application::on_key_up(RedKey key)
_inputs_handler->on_key_up(scan_code);
}
+void Application::on_key_up(RedKey key)
+{
+ if(key < 0 || key >= REDKEY_NUM_KEYS || !_key_table[key].press) {
+ return;
+ }
+
+ if (_sticky_info.trace_is_on) {
+ ASSERT(_sticky_info.sticky_mode || (key == _sticky_info.key) ||
+ (_sticky_info.key == REDKEY_INVALID));
+ if (key == _sticky_info.key) {
+ _sticky_info.key_down = false;
+ if (_sticky_info.key_first_down) {
+ _sticky_info.key_first_down = false;
+ if (!_sticky_info.sticky_mode) {
+ reset_sticky();
+ } else {
+ return; // ignore the sticky-key first release
+ }
+ }
+ }
+
+ if (_sticky_info.sticky_mode) {
+ RedKey old_sticky_key = _sticky_info.key;
+ reset_sticky();
+ if (key == old_sticky_key) {
+ return; // no need to send key_up twice
+ }
+ }
+ }
+
+ do_on_key_up(key);
+ }
+
void Application::on_deactivate_screen(RedScreen* screen)
{
if (_active_screen == screen) {
+ _sys_key_intercept_mode = false;
release_capture();
_active_screen = NULL;
}
@@ -858,9 +1034,26 @@ void Application::on_deactivate_screen(RedScreen* screen)
void Application::on_activate_screen(RedScreen* screen)
{
+ ASSERT(!_active_screen || (_active_screen == screen));
_active_screen = screen;
}
+void Application::on_start_screen_key_interception(RedScreen* screen)
+{
+ ASSERT(screen == _active_screen);
+
+ _sys_key_intercept_mode = true;
+ reset_sticky();
+}
+
+void Application::on_stop_screen_key_interception(RedScreen* screen)
+{
+ ASSERT(screen == _active_screen);
+
+ _sys_key_intercept_mode = false;
+ reset_sticky();
+}
+
void Application::on_app_activated()
{
_active = true;
@@ -997,6 +1190,7 @@ void Application::show_full_screen()
void Application::enter_full_screen()
{
+ LOG_INFO("");
_changing_screens = true;
release_capture();
assign_monitors();
@@ -1015,6 +1209,7 @@ void Application::exit_full_screen()
if (!_full_screen) {
return;
}
+ LOG_INFO("");
release_capture();
for (int i = 0; i < (int)_screens.size(); i++) {
if (_screens[i]) {
@@ -1088,7 +1283,9 @@ void Application::show_splash(int screen_id)
if (screen_id != 0) {
return;
}
+ _splash_mode = true;
release_capture();
+ ASSERT(!_sticky_info.trace_is_on);
(*_gui_layer).set_splash_mode();
}
@@ -1097,7 +1294,9 @@ void Application::hide_splash(int screen_id)
if (screen_id != 0) {
return;
}
+ _splash_mode = false;
(*_gui_layer).set_info_mode();
+ reset_sticky();
}
uint32_t Application::get_mouse_mode()