summaryrefslogtreecommitdiffstats
path: root/client/x11/red_window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/x11/red_window.cpp')
-rw-r--r--client/x11/red_window.cpp1917
1 files changed, 1917 insertions, 0 deletions
diff --git a/client/x11/red_window.cpp b/client/x11/red_window.cpp
new file mode 100644
index 00000000..9cce15f6
--- /dev/null
+++ b/client/x11/red_window.cpp
@@ -0,0 +1,1917 @@
+/*
+ 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/>.
+*/
+
+#define GL_GLEXT_PROTOTYPES
+#include "common.h"
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/keysymdef.h>
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glx.h>
+#include <GL/glext.h>
+#include <stdio.h>
+
+#include "red_window.h"
+#include "utils.h"
+#include "gl_utils.h"
+#include "debug.h"
+#include "platform.h"
+#include "x_platform.h"
+#include "pixels_source_p.h"
+#include "red.h"
+#include "region.h"
+#include "red_pixmap_gl.h"
+#include "red_pixmap_gl.h"
+#include "x_icon.h"
+
+
+#define X_RETRIES 10
+#define X_RETRY_DELAY_MICRO (1000 * 100)
+#define RAISE_RETRIES X_RETRIES
+#define Z_POSITION_RETRIES X_RETRIES
+#define GET_POSITION_RETRIES X_RETRIES
+#define GET_VIS_REGION_RETRIES X_RETRIES
+#define MOUSE_GRAB_RETRIES X_RETRIES
+
+static Display* x_display = NULL;
+static XContext user_data_context;
+static bool using_evdev = false;
+
+static Atom wm_protocol_atom;
+static Atom wm_delete_window_atom;
+
+static Atom wm_desktop;
+static Atom wm_current_desktop;
+
+static Atom wm_state;
+static Atom wm_state_above;
+static Atom wm_state_fullscreen;
+
+static Atom wm_user_time;
+
+#define USE_X11_KEYCODE
+
+#ifdef USE_X11_KEYCODE
+
+enum EvdevKeyCode {
+ EVDEV_KEYCODE_ESCAPE = 9,
+ EVDEV_KEYCODE_1,
+ EVDEV_KEYCODE_2,
+ EVDEV_KEYCODE_3,
+ EVDEV_KEYCODE_4,
+ EVDEV_KEYCODE_5,
+ EVDEV_KEYCODE_6,
+ EVDEV_KEYCODE_7,
+ EVDEV_KEYCODE_8,
+ EVDEV_KEYCODE_9,
+ EVDEV_KEYCODE_0,
+ EVDEV_KEYCODE_MINUS,
+ EVDEV_KEYCODE_EQUAL,
+ EVDEV_KEYCODE_BACK_SPACE,
+ EVDEV_KEYCODE_TAB,
+ EVDEV_KEYCODE_Q,
+ EVDEV_KEYCODE_W,
+ EVDEV_KEYCODE_E,
+ EVDEV_KEYCODE_R,
+ EVDEV_KEYCODE_T,
+ EVDEV_KEYCODE_Y,
+ EVDEV_KEYCODE_U,
+ EVDEV_KEYCODE_I,
+ EVDEV_KEYCODE_O,
+ EVDEV_KEYCODE_P,
+ EVDEV_KEYCODE_L_BRACKET,
+ EVDEV_KEYCODE_R_BRACKET,
+ EVDEV_KEYCODE_RETURN,
+ EVDEV_KEYCODE_L_CONTROL,
+ EVDEV_KEYCODE_A,
+ EVDEV_KEYCODE_S,
+ EVDEV_KEYCODE_D,
+ EVDEV_KEYCODE_F,
+ EVDEV_KEYCODE_G,
+ EVDEV_KEYCODE_H,
+ EVDEV_KEYCODE_J,
+ EVDEV_KEYCODE_K,
+ EVDEV_KEYCODE_L,
+ EVDEV_KEYCODE_SEMICOLON,
+ EVDEV_KEYCODE_APOSTROPH,
+ EVDEV_KEYCODE_BACKQUAT,
+ EVDEV_KEYCODE_L_SHIFT,
+ EVDEV_KEYCODE_BACKSLASH,
+ EVDEV_KEYCODE_Z,
+ EVDEV_KEYCODE_X,
+ EVDEV_KEYCODE_C,
+ EVDEV_KEYCODE_V,
+ EVDEV_KEYCODE_B,
+ EVDEV_KEYCODE_N,
+ EVDEV_KEYCODE_M,
+ EVDEV_KEYCODE_COMMA,
+ EVDEV_KEYCODE_PERIOD,
+ EVDEV_KEYCODE_SLASH,
+ EVDEV_KEYCODE_R_SHIFT,
+ EVDEV_KEYCODE_PAD_MULTIPLY,
+ EVDEV_KEYCODE_L_ALT,
+ EVDEV_KEYCODE_SPACE,
+ EVDEV_KEYCODE_CAPS_LOCK,
+ EVDEV_KEYCODE_F1,
+ EVDEV_KEYCODE_F2,
+ EVDEV_KEYCODE_F3,
+ EVDEV_KEYCODE_F4,
+ EVDEV_KEYCODE_F5,
+ EVDEV_KEYCODE_F6,
+ EVDEV_KEYCODE_F7,
+ EVDEV_KEYCODE_F8,
+ EVDEV_KEYCODE_F9,
+ EVDEV_KEYCODE_F10,
+ EVDEV_KEYCODE_NUM_LOCK,
+ EVDEV_KEYCODE_SCROLL_LOCK,
+ EVDEV_KEYCODE_PAD_7,
+ EVDEV_KEYCODE_PAD_8,
+ EVDEV_KEYCODE_PAD_9,
+ EVDEV_KEYCODE_PAD_SUBTRACT,
+ EVDEV_KEYCODE_PAD_4,
+ EVDEV_KEYCODE_PAD_5,
+ EVDEV_KEYCODE_PAD_6,
+ EVDEV_KEYCODE_PAD_ADD,
+ EVDEV_KEYCODE_PAD_1,
+ EVDEV_KEYCODE_PAD_2,
+ EVDEV_KEYCODE_PAD_3,
+ EVDEV_KEYCODE_PAD_0,
+ EVDEV_KEYCODE_PAD_DEL,
+ EVDEV_KEYCODE_EUROPEAN = 94,
+ EVDEV_KEYCODE_F11,
+ EVDEV_KEYCODE_F12,
+ EVDEV_KEYCODE_JAPANESE_BACKSLASH,
+ EVDEV_KEYCODE_JAPANESE_HENKAN = 100,
+ EVDEV_KEYCODE_JAPANESE_HIRAGANA_KATAKANA,
+ EVDEV_KEYCODE_JAPANESE_MUHENKAN,
+ EVDEV_KEYCODE_PAD_ENTER = 104,
+ EVDEV_KEYCODE_R_CONTROL,
+ EVDEV_KEYCODE_PAD_DEVIDE,
+ EVDEV_KEYCODE_PRINT,
+ EVDEV_KEYCODE_R_ALT,
+ EVDEV_KEYCODE_HOME = 110,
+ EVDEV_KEYCODE_UP,
+ EVDEV_KEYCODE_PAGE_UP,
+ EVDEV_KEYCODE_LEFT,
+ EVDEV_KEYCODE_RIGHT,
+ EVDEV_KEYCODE_END,
+ EVDEV_KEYCODE_DOWN,
+ EVDEV_KEYCODE_PAGE_DOWN,
+ EVDEV_KEYCODE_INSERT,
+ EVDEV_KEYCODE_DELETE,
+ EVDEV_KEYCODE_PAUSE = 127,
+ EVDEV_KEYCODE_HANGUL = 130,
+ EVDEV_KEYCODE_HANGUL_HANJA,
+ EVDEV_KEYCODE_YEN,
+ EVDEV_KEYCODE_L_COMMAND,
+ EVDEV_KEYCODE_R_COMMAND,
+ EVDEV_KEYCODE_MENU,
+};
+
+enum KbdKeyCode {
+ KBD_KEYCODE_ESCAPE = 9,
+ KBD_KEYCODE_1,
+ KBD_KEYCODE_2,
+ KBD_KEYCODE_3,
+ KBD_KEYCODE_4,
+ KBD_KEYCODE_5,
+ KBD_KEYCODE_6,
+ KBD_KEYCODE_7,
+ KBD_KEYCODE_8,
+ KBD_KEYCODE_9,
+ KBD_KEYCODE_0,
+ KBD_KEYCODE_MINUS,
+ KBD_KEYCODE_EQUAL,
+ KBD_KEYCODE_BACK_SPACE,
+ KBD_KEYCODE_TAB,
+ KBD_KEYCODE_Q,
+ KBD_KEYCODE_W,
+ KBD_KEYCODE_E,
+ KBD_KEYCODE_R,
+ KBD_KEYCODE_T,
+ KBD_KEYCODE_Y,
+ KBD_KEYCODE_U,
+ KBD_KEYCODE_I,
+ KBD_KEYCODE_O,
+ KBD_KEYCODE_P,
+ KBD_KEYCODE_L_BRACKET,
+ KBD_KEYCODE_R_BRACKET,
+ KBD_KEYCODE_RETURN,
+ KBD_KEYCODE_L_CONTROL,
+ KBD_KEYCODE_A,
+ KBD_KEYCODE_S,
+ KBD_KEYCODE_D,
+ KBD_KEYCODE_F,
+ KBD_KEYCODE_G,
+ KBD_KEYCODE_H,
+ KBD_KEYCODE_J,
+ KBD_KEYCODE_K,
+ KBD_KEYCODE_L,
+ KBD_KEYCODE_SEMICOLON,
+ KBD_KEYCODE_APOSTROPH,
+ KBD_KEYCODE_BACKQUAT,
+ KBD_KEYCODE_L_SHIFT,
+ KBD_KEYCODE_BACKSLASH,
+ KBD_KEYCODE_Z,
+ KBD_KEYCODE_X,
+ KBD_KEYCODE_C,
+ KBD_KEYCODE_V,
+ KBD_KEYCODE_B,
+ KBD_KEYCODE_N,
+ KBD_KEYCODE_M,
+ KBD_KEYCODE_COMMA,
+ KBD_KEYCODE_PERIOD,
+ KBD_KEYCODE_SLASH,
+ KBD_KEYCODE_R_SHIFT,
+ KBD_KEYCODE_PAD_MULTIPLY,
+ KBD_KEYCODE_L_ALT,
+ KBD_KEYCODE_SPACE,
+ KBD_KEYCODE_CAPS_LOCK,
+ KBD_KEYCODE_F1,
+ KBD_KEYCODE_F2,
+ KBD_KEYCODE_F3,
+ KBD_KEYCODE_F4,
+ KBD_KEYCODE_F5,
+ KBD_KEYCODE_F6,
+ KBD_KEYCODE_F7,
+ KBD_KEYCODE_F8,
+ KBD_KEYCODE_F9,
+ KBD_KEYCODE_F10,
+ KBD_KEYCODE_NUM_LOCK,
+ KBD_KEYCODE_SCROLL_LOCK,
+ KBD_KEYCODE_PAD_7,
+ KBD_KEYCODE_PAD_8,
+ KBD_KEYCODE_PAD_9,
+ KBD_KEYCODE_PAD_SUBTRACT,
+ KBD_KEYCODE_PAD_4,
+ KBD_KEYCODE_PAD_5,
+ KBD_KEYCODE_PAD_6,
+ KBD_KEYCODE_PAD_ADD,
+ KBD_KEYCODE_PAD_1,
+ KBD_KEYCODE_PAD_2,
+ KBD_KEYCODE_PAD_3,
+ KBD_KEYCODE_PAD_0,
+ KBD_KEYCODE_PAD_DEL,
+ KBD_KEYCODE_EUROPEAN = 94,
+ KBD_KEYCODE_F11,
+ KBD_KEYCODE_F12,
+ KBD_KEYCODE_HOME,
+ KBD_KEYCODE_UP,
+ KBD_KEYCODE_PAGE_UP,
+ KBD_KEYCODE_LEFT,
+ KBD_KEYCODE_RIGHT = 102,
+ KBD_KEYCODE_END,
+ KBD_KEYCODE_DOWN,
+ KBD_KEYCODE_PAGE_DOWN,
+ KBD_KEYCODE_INSERT,
+ KBD_KEYCODE_DELETE,
+ KBD_KEYCODE_PAD_ENTER,
+ KBD_KEYCODE_R_CONTROL,
+ KBD_KEYCODE_PAUSE,
+ KBD_KEYCODE_PRINT,
+ KBD_KEYCODE_PAD_DEVIDE,
+ KBD_KEYCODE_R_ALT,
+ KBD_KEYCODE_L_COMMAND = 115,
+ KBD_KEYCODE_R_COMMAND,
+ KBD_KEYCODE_MENU,
+ KBD_KEYCODE_JAPANESE_HENKAN = 129,
+ KBD_KEYCODE_JAPANESE_MUHENKAN = 131,
+ KBD_KEYCODE_YEN = 133,
+ KBD_KEYCODE_JAPANESE_HIRAGANA_KATAKANA = 208,
+ KBD_KEYCODE_HANGUL_HANJA,
+ KBD_KEYCODE_HANGUL,
+ KBD_KEYCODE_JAPANESE_BACKSLASH,
+};
+
+static void query_keyboard()
+{
+ XkbDescPtr kbd_desk = XkbGetKeyboard(x_display, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (!kbd_desk) {
+ LOG_WARN("get keyboard failed");
+ return;
+ }
+
+ char* keycodes = XGetAtomName(x_display, kbd_desk->names->keycodes);
+
+ if (keycodes) {
+ if (strstr(keycodes, "evdev")) {
+ using_evdev = true;
+ }
+ XFree(keycodes);
+ } else {
+ LOG_WARN("get name failed");
+ }
+ XkbFreeClientMap(kbd_desk, XkbAllComponentsMask, True);
+}
+
+static RedKey keycode_map[256];
+
+#define INIT_MAP \
+ KEYMAP(KEYCODE_ESCAPE, REDKEY_ESCAPE); \
+ KEYMAP(KEYCODE_1, REDKEY_1); \
+ KEYMAP(KEYCODE_2, REDKEY_2); \
+ KEYMAP(KEYCODE_3, REDKEY_3); \
+ KEYMAP(KEYCODE_4, REDKEY_4); \
+ KEYMAP(KEYCODE_5, REDKEY_5); \
+ KEYMAP(KEYCODE_6, REDKEY_6); \
+ KEYMAP(KEYCODE_7, REDKEY_7); \
+ KEYMAP(KEYCODE_8, REDKEY_8); \
+ KEYMAP(KEYCODE_9, REDKEY_9); \
+ KEYMAP(KEYCODE_0, REDKEY_0); \
+ KEYMAP(KEYCODE_MINUS, REDKEY_MINUS); \
+ KEYMAP(KEYCODE_EQUAL, REDKEY_EQUALS); \
+ KEYMAP(KEYCODE_BACK_SPACE, REDKEY_BACKSPACE); \
+ KEYMAP(KEYCODE_TAB, REDKEY_TAB); \
+ KEYMAP(KEYCODE_Q, REDKEY_Q); \
+ KEYMAP(KEYCODE_W, REDKEY_W); \
+ KEYMAP(KEYCODE_E, REDKEY_E); \
+ KEYMAP(KEYCODE_R, REDKEY_R); \
+ KEYMAP(KEYCODE_T, REDKEY_T); \
+ KEYMAP(KEYCODE_Y, REDKEY_Y); \
+ KEYMAP(KEYCODE_U, REDKEY_U); \
+ KEYMAP(KEYCODE_I, REDKEY_I); \
+ KEYMAP(KEYCODE_O, REDKEY_O); \
+ KEYMAP(KEYCODE_P, REDKEY_P); \
+ KEYMAP(KEYCODE_L_BRACKET, REDKEY_L_BRACKET); \
+ KEYMAP(KEYCODE_R_BRACKET, REDKEY_R_BRACKET); \
+ KEYMAP(KEYCODE_RETURN, REDKEY_ENTER); \
+ KEYMAP(KEYCODE_L_CONTROL, REDKEY_L_CTRL); \
+ KEYMAP(KEYCODE_A, REDKEY_A); \
+ KEYMAP(KEYCODE_S, REDKEY_S); \
+ KEYMAP(KEYCODE_D, REDKEY_D); \
+ KEYMAP(KEYCODE_F, REDKEY_F); \
+ KEYMAP(KEYCODE_G, REDKEY_G); \
+ KEYMAP(KEYCODE_H, REDKEY_H); \
+ KEYMAP(KEYCODE_J, REDKEY_J); \
+ KEYMAP(KEYCODE_K, REDKEY_K); \
+ KEYMAP(KEYCODE_L, REDKEY_L); \
+ KEYMAP(KEYCODE_SEMICOLON, REDKEY_SEMICOLON); \
+ KEYMAP(KEYCODE_APOSTROPH, REDKEY_QUOTE); \
+ KEYMAP(KEYCODE_BACKQUAT, REDKEY_BACK_QUOTE); \
+ KEYMAP(KEYCODE_L_SHIFT, REDKEY_L_SHIFT); \
+ KEYMAP(KEYCODE_BACKSLASH, REDKEY_BACK_SLASH); \
+ KEYMAP(KEYCODE_Z, REDKEY_Z); \
+ KEYMAP(KEYCODE_X, REDKEY_X); \
+ KEYMAP(KEYCODE_C, REDKEY_C); \
+ KEYMAP(KEYCODE_V, REDKEY_V); \
+ KEYMAP(KEYCODE_B, REDKEY_B); \
+ KEYMAP(KEYCODE_N, REDKEY_N); \
+ KEYMAP(KEYCODE_M, REDKEY_M); \
+ KEYMAP(KEYCODE_COMMA, REDKEY_COMMA); \
+ KEYMAP(KEYCODE_PERIOD, REDKEY_PERIOD); \
+ KEYMAP(KEYCODE_SLASH, REDKEY_SLASH); \
+ KEYMAP(KEYCODE_R_SHIFT, REDKEY_R_SHIFT); \
+ KEYMAP(KEYCODE_PAD_MULTIPLY, REDKEY_PAD_MULTIPLY); \
+ KEYMAP(KEYCODE_L_ALT, REDKEY_L_ALT); \
+ KEYMAP(KEYCODE_SPACE, REDKEY_SPACE); \
+ KEYMAP(KEYCODE_CAPS_LOCK, REDKEY_CAPS_LOCK); \
+ KEYMAP(KEYCODE_F1, REDKEY_F1); \
+ KEYMAP(KEYCODE_F2, REDKEY_F2); \
+ KEYMAP(KEYCODE_F3, REDKEY_F3); \
+ KEYMAP(KEYCODE_F4, REDKEY_F4); \
+ KEYMAP(KEYCODE_F5, REDKEY_F5); \
+ KEYMAP(KEYCODE_F6, REDKEY_F6); \
+ KEYMAP(KEYCODE_F7, REDKEY_F7); \
+ KEYMAP(KEYCODE_F8, REDKEY_F8); \
+ KEYMAP(KEYCODE_F9, REDKEY_F9); \
+ KEYMAP(KEYCODE_F10, REDKEY_F10); \
+ KEYMAP(KEYCODE_NUM_LOCK, REDKEY_NUM_LOCK); \
+ KEYMAP(KEYCODE_SCROLL_LOCK, REDKEY_SCROLL_LOCK); \
+ KEYMAP(KEYCODE_PAD_7, REDKEY_PAD_7); \
+ KEYMAP(KEYCODE_PAD_8, REDKEY_PAD_8); \
+ KEYMAP(KEYCODE_PAD_9, REDKEY_PAD_9); \
+ KEYMAP(KEYCODE_PAD_SUBTRACT, REDKEY_PAD_MINUS); \
+ KEYMAP(KEYCODE_PAD_4, REDKEY_PAD_4); \
+ KEYMAP(KEYCODE_PAD_5, REDKEY_PAD_5); \
+ KEYMAP(KEYCODE_PAD_6, REDKEY_PAD_6); \
+ KEYMAP(KEYCODE_PAD_ADD, REDKEY_PAD_PLUS); \
+ KEYMAP(KEYCODE_PAD_1, REDKEY_PAD_1); \
+ KEYMAP(KEYCODE_PAD_2, REDKEY_PAD_2); \
+ KEYMAP(KEYCODE_PAD_3, REDKEY_PAD_3); \
+ KEYMAP(KEYCODE_PAD_0, REDKEY_PAD_0); \
+ KEYMAP(KEYCODE_PAD_DEL, REDKEY_PAD_POINT); \
+ KEYMAP(KEYCODE_EUROPEAN, REDKEY_EUROPEAN); \
+ KEYMAP(KEYCODE_F11, REDKEY_F11); \
+ KEYMAP(KEYCODE_F12, REDKEY_F12); \
+ KEYMAP(KEYCODE_JAPANESE_BACKSLASH, REDKEY_JAPANESE_BACKSLASH); \
+ KEYMAP(KEYCODE_JAPANESE_HENKAN, REDKEY_JAPANESE_HENKAN); \
+ KEYMAP(KEYCODE_JAPANESE_HIRAGANA_KATAKANA, REDKEY_JAPANESE_HIRAGANA_KATAKANA); \
+ KEYMAP(KEYCODE_JAPANESE_MUHENKAN, REDKEY_JAPANESE_MUHENKAN); \
+ KEYMAP(KEYCODE_PAD_ENTER, REDKEY_PAD_ENTER); \
+ KEYMAP(KEYCODE_R_CONTROL, REDKEY_R_CTRL); \
+ KEYMAP(KEYCODE_PAD_DEVIDE, REDKEY_PAD_DIVIDE); \
+ KEYMAP(KEYCODE_PRINT, REDKEY_CTRL_PRINT_SCREEN); \
+ KEYMAP(KEYCODE_R_ALT, REDKEY_R_ALT); \
+ KEYMAP(KEYCODE_HOME, REDKEY_HOME); \
+ KEYMAP(KEYCODE_UP, REDKEY_UP); \
+ KEYMAP(KEYCODE_PAGE_UP, REDKEY_PAGEUP); \
+ KEYMAP(KEYCODE_LEFT, REDKEY_LEFT); \
+ KEYMAP(KEYCODE_RIGHT, REDKEY_RIGHT); \
+ KEYMAP(KEYCODE_END, REDKEY_END); \
+ KEYMAP(KEYCODE_DOWN, REDKEY_DOWN); \
+ KEYMAP(KEYCODE_PAGE_DOWN, REDKEY_PAGEDOWN); \
+ KEYMAP(KEYCODE_INSERT, REDKEY_INSERT); \
+ KEYMAP(KEYCODE_DELETE, REDKEY_DELETE); \
+ KEYMAP(KEYCODE_PAUSE, REDKEY_PAUSE); \
+ \
+ KEYMAP(KEYCODE_YEN, REDKEY_JAPANESE_YEN); \
+ KEYMAP(KEYCODE_L_COMMAND, REDKEY_LEFT_CMD); \
+ KEYMAP(KEYCODE_R_COMMAND, REDKEY_RIGHT_CMD); \
+ KEYMAP(KEYCODE_MENU, REDKEY_MENU); \
+ KEYMAP(KEYCODE_HANGUL, REDKEY_KOREAN_HANGUL); \
+ KEYMAP(KEYCODE_HANGUL_HANJA, REDKEY_KOREAN_HANGUL_HANJA);
+
+static void init_evdev_map()
+{
+ #define KEYMAP(key_code, red_key) keycode_map[EVDEV_##key_code] = red_key
+ INIT_MAP;
+ #undef KEYMAP
+}
+
+static void init_kbd_map()
+{
+ #define KEYMAP(key_code, red_key) keycode_map[KBD_##key_code] = red_key
+ INIT_MAP;
+ #undef KEYMAP
+}
+
+static void init_key_map()
+{
+ query_keyboard();
+ memset(keycode_map, 0, sizeof(keycode_map));
+ if (using_evdev) {
+ LOG_INFO("using evdev mapping");
+ init_evdev_map();
+ } else {
+ LOG_INFO("using kbd mapping");
+ init_kbd_map();
+ }
+}
+
+static inline RedKey to_red_key_code(unsigned int keycode)
+{
+ if (keycode > 255) {
+ return REDKEY_INVALID;
+ }
+ return keycode_map[keycode];
+}
+
+#else
+
+static RedKey key_table_0xff[256]; //miscellany
+
+static RedKey key_table_0x00[256]; //Latin 1
+
+static RedKey key_table_0xfe[256]; //Keyboard (XKB) Extension
+
+#define INIT_KEY(x, red) key_table_0xff[x & 0xff] = red;
+
+static void init_key_table_0xff()
+{
+ for (int i = 0; i < sizeof(key_table_0xff) / sizeof(key_table_0xff[0]); i++) {
+ key_table_0xff[i] = REDKEY_INVALID;
+ }
+
+ INIT_KEY(XK_Escape, REDKEY_ESCAPE);
+ INIT_KEY(XK_BackSpace, REDKEY_BACKSPACE);
+ INIT_KEY(XK_Tab, REDKEY_TAB);
+ INIT_KEY(XK_Return, REDKEY_ENTER);
+ INIT_KEY(XK_Control_L, REDKEY_L_CTRL);
+ INIT_KEY(XK_Shift_L, REDKEY_L_SHIFT);
+ INIT_KEY(XK_Shift_R, REDKEY_R_SHIFT);
+ INIT_KEY(XK_KP_Multiply, REDKEY_PAD_MULTIPLY);
+ INIT_KEY(XK_Alt_L, REDKEY_L_ALT);
+ INIT_KEY(XK_Caps_Lock, REDKEY_CAPS_LOCK);
+ INIT_KEY(XK_F1, REDKEY_F1);
+ INIT_KEY(XK_F2, REDKEY_F2);
+ INIT_KEY(XK_F3, REDKEY_F3);
+ INIT_KEY(XK_F4, REDKEY_F4);
+ INIT_KEY(XK_F5, REDKEY_F5);
+ INIT_KEY(XK_F6, REDKEY_F6);
+ INIT_KEY(XK_F7, REDKEY_F7);
+ INIT_KEY(XK_F8, REDKEY_F8);
+ INIT_KEY(XK_F9, REDKEY_F9);
+ INIT_KEY(XK_F10, REDKEY_F10);
+
+ INIT_KEY(XK_Num_Lock, REDKEY_NUM_LOCK);
+ INIT_KEY(XK_Scroll_Lock, REDKEY_SCROLL_LOCK);
+ INIT_KEY(XK_KP_7, REDKEY_PAD_7);
+ INIT_KEY(XK_KP_Home, REDKEY_PAD_7);
+ INIT_KEY(XK_KP_8, REDKEY_PAD_8);
+ INIT_KEY(XK_KP_Up, REDKEY_PAD_8);
+ INIT_KEY(XK_KP_9, REDKEY_PAD_9);
+ INIT_KEY(XK_KP_Page_Up, REDKEY_PAD_9);
+ INIT_KEY(XK_KP_Subtract, REDKEY_PAD_MINUS);
+ INIT_KEY(XK_KP_4, REDKEY_PAD_4);
+ INIT_KEY(XK_KP_Left, REDKEY_PAD_4);
+ INIT_KEY(XK_KP_5, REDKEY_PAD_5);
+ INIT_KEY(XK_KP_Begin, REDKEY_PAD_5);
+ INIT_KEY(XK_KP_6, REDKEY_PAD_6);
+ INIT_KEY(XK_KP_Right, REDKEY_PAD_6);
+ INIT_KEY(XK_KP_Add, REDKEY_PAD_PLUS);
+ INIT_KEY(XK_KP_1, REDKEY_PAD_1);
+ INIT_KEY(XK_KP_End, REDKEY_PAD_1);
+ INIT_KEY(XK_KP_2, REDKEY_PAD_2);
+ INIT_KEY(XK_KP_Down, REDKEY_PAD_2);
+ INIT_KEY(XK_KP_3, REDKEY_PAD_3);
+ INIT_KEY(XK_KP_Page_Down, REDKEY_PAD_3);
+ INIT_KEY(XK_KP_0, REDKEY_PAD_0);
+ INIT_KEY(XK_KP_Insert, REDKEY_PAD_0);
+ INIT_KEY(XK_KP_Decimal, REDKEY_PAD_POINT);
+ INIT_KEY(XK_KP_Delete, REDKEY_PAD_POINT);
+ INIT_KEY(XK_F11, REDKEY_F11);
+ INIT_KEY(XK_F12, REDKEY_F12);
+
+ INIT_KEY(XK_KP_Enter, REDKEY_PAD_ENTER);
+ INIT_KEY(XK_Control_R, REDKEY_R_CTRL);
+ INIT_KEY(XK_KP_Divide, REDKEY_PAD_DIVIDE);
+ INIT_KEY(XK_Print, REDKEY_CTRL_PRINT_SCREEN);
+
+ INIT_KEY(XK_Home, REDKEY_HOME);
+ INIT_KEY(XK_Up, REDKEY_UP);
+ INIT_KEY(XK_Page_Up, REDKEY_PAGEUP);
+ INIT_KEY(XK_Left, REDKEY_LEFT);
+ INIT_KEY(XK_Right, REDKEY_RIGHT);
+ INIT_KEY(XK_End, REDKEY_END);
+
+ INIT_KEY(XK_Down, REDKEY_DOWN);
+ INIT_KEY(XK_Page_Down, REDKEY_PAGEDOWN);
+ INIT_KEY(XK_Insert, REDKEY_INSERT);
+ INIT_KEY(XK_Delete, REDKEY_DELETE);
+ INIT_KEY(XK_Super_L, REDKEY_LEFT_CMD);
+ INIT_KEY(XK_Super_R, REDKEY_RIGHT_CMD);
+ INIT_KEY(XK_Menu, REDKEY_MENU);
+ INIT_KEY(XK_Pause, REDKEY_CTRL_BREAK);
+}
+
+#undef INIT_KEY
+#define INIT_KEY(x, red) key_table_0x00[x & 0xff] = red;
+
+static void init_key_table_0x00()
+{
+ for (int i = 0; i < sizeof(key_table_0x00) / sizeof(key_table_0x00[0]); i++) {
+ key_table_0x00[i] = REDKEY_INVALID;
+ }
+
+ INIT_KEY(XK_1, REDKEY_1);
+ INIT_KEY(XK_2, REDKEY_2);
+ INIT_KEY(XK_3, REDKEY_3);
+ INIT_KEY(XK_4, REDKEY_4);
+ INIT_KEY(XK_5, REDKEY_5);
+ INIT_KEY(XK_6, REDKEY_6);
+ INIT_KEY(XK_7, REDKEY_7);
+ INIT_KEY(XK_8, REDKEY_8);
+ INIT_KEY(XK_9, REDKEY_9);
+ INIT_KEY(XK_0, REDKEY_0);
+ INIT_KEY(XK_minus, REDKEY_MINUS);
+ INIT_KEY(XK_equal, REDKEY_EQUALS);
+ INIT_KEY(XK_q, REDKEY_Q);
+ INIT_KEY(XK_w, REDKEY_W);
+ INIT_KEY(XK_e, REDKEY_E);
+ INIT_KEY(XK_r, REDKEY_R);
+ INIT_KEY(XK_t, REDKEY_T);
+ INIT_KEY(XK_y, REDKEY_Y);
+ INIT_KEY(XK_u, REDKEY_U);
+ INIT_KEY(XK_i, REDKEY_I);
+ INIT_KEY(XK_o, REDKEY_O);
+ INIT_KEY(XK_p, REDKEY_P);
+ INIT_KEY(XK_bracketleft, REDKEY_L_BRACKET);
+ INIT_KEY(XK_bracketright, REDKEY_R_BRACKET);
+ INIT_KEY(XK_a, REDKEY_A);
+ INIT_KEY(XK_s, REDKEY_S);
+ INIT_KEY(XK_e, REDKEY_E);
+ INIT_KEY(XK_d, REDKEY_D);
+ INIT_KEY(XK_f, REDKEY_F);
+ INIT_KEY(XK_g, REDKEY_G);
+ INIT_KEY(XK_h, REDKEY_H);
+ INIT_KEY(XK_j, REDKEY_J);
+ INIT_KEY(XK_k, REDKEY_K);
+ INIT_KEY(XK_l, REDKEY_L);
+ INIT_KEY(XK_semicolon, REDKEY_SEMICOLON);
+ INIT_KEY(XK_quoteright, REDKEY_QUOTE);
+ INIT_KEY(XK_quoteleft, REDKEY_BACK_QUOTE);
+ INIT_KEY(XK_backslash, REDKEY_BACK_SLASH);
+ INIT_KEY(XK_z, REDKEY_Z);
+ INIT_KEY(XK_x, REDKEY_X);
+ INIT_KEY(XK_c, REDKEY_C);
+ INIT_KEY(XK_v, REDKEY_V);
+ INIT_KEY(XK_b, REDKEY_B);
+ INIT_KEY(XK_n, REDKEY_N);
+ INIT_KEY(XK_m, REDKEY_M);
+ INIT_KEY(XK_comma, REDKEY_COMMA);
+ INIT_KEY(XK_period, REDKEY_PERIOD);
+ INIT_KEY(XK_slash, REDKEY_SLASH);
+ INIT_KEY(XK_space, REDKEY_SPACE);
+}
+
+#undef INIT_KEY
+#define INIT_KEY(x, red) key_table_0xfe[x & 0xff] = red;
+
+static void init_key_table_0xfe()
+{
+ for (int i = 0; i < sizeof(key_table_0xfe) / sizeof(key_table_0xfe[0]); i++) {
+ key_table_0xfe[i] = REDKEY_INVALID;
+ }
+
+ INIT_KEY(XK_ISO_Level3_Shift, REDKEY_R_ALT);
+}
+
+static inline RedKey to_red_key_code(unsigned int keycode)
+{
+ KeySym sym = XKeycodeToKeysym(x_display, keycode, 0);
+ RedKey red_key;
+
+ if (sym == NoSymbol) {
+ DBG(0, "no symbol for %d", keycode);
+ }
+
+ switch (sym >> 8) {
+ case 0x00:
+ red_key = key_table_0x00[sym & 0xff];
+ break;
+ case 0xff:
+ red_key = key_table_0xff[sym & 0xff];
+ break;
+ case 0xfe:
+ red_key = key_table_0xfe[sym & 0xff];
+ break;
+ default:
+ DBG(0, "unsupported key set %lu", (sym >> 8));
+ return REDKEY_INVALID;
+ }
+
+ if (red_key == REDKEY_INVALID) {
+ DBG(0, "no valid key mapping for keycode %u", keycode);
+ }
+ return red_key;
+}
+
+#endif
+
+static inline int to_red_buttons_state(unsigned int state)
+{
+ return ((state & Button1Mask) ? REDC_LBUTTON_MASK : 0) |
+ ((state & Button2Mask) ? REDC_MBUTTON_MASK : 0) |
+ ((state & Button3Mask) ? REDC_RBUTTON_MASK : 0);
+}
+
+static inline RedButton to_red_button(unsigned int botton, unsigned int& state, bool press)
+{
+ unsigned int mask = 0;
+ RedButton ret;
+
+ switch (botton) {
+ case Button1:
+ mask = REDC_LBUTTON_MASK;
+ ret = REDC_MOUSE_LBUTTON;
+ break;
+ case Button2:
+ mask = REDC_MBUTTON_MASK;
+ ret = REDC_MOUSE_MBUTTON;
+ break;
+ case Button3:
+ mask = REDC_RBUTTON_MASK;
+ ret = REDC_MOUSE_RBUTTON;
+ break;
+ case Button4:
+ ret = REDC_MOUSE_UBUTTON;
+ break;
+ case Button5:
+ ret = REDC_MOUSE_DBUTTON;
+ break;
+ default:
+ ret = REDC_MOUSE_INVALID_BUTTON;
+ }
+ if (press) {
+ state |= mask;
+ } else {
+ state &= ~mask;
+ }
+ return ret;
+}
+
+void RedWindow_p::win_proc(XEvent& event)
+{
+ XPointer window_pointer;
+ RedWindow* red_window;
+
+ if (XFindContext(x_display, event.xany.window, user_data_context, &window_pointer)) {
+ THROW("no user data");
+ }
+ red_window = (RedWindow*)window_pointer;
+ switch (event.type) {
+ case MotionNotify: {
+ Point size = red_window->get_size();
+ if (event.xmotion.x >= 0 && event.xmotion.y >= 0 &&
+ event.xmotion.x < size.x && event.xmotion.y < size.y) {
+ Point origin = red_window->get_origin();
+ red_window->get_listener().on_mouse_motion(event.xmotion.x - origin.x,
+ event.xmotion.y - origin.y,
+ to_red_buttons_state(event.xmotion.state));
+ }
+ break;
+ }
+ case KeyPress:
+ red_window->get_listener().on_key_press(to_red_key_code(event.xkey.keycode));
+ break;
+ case KeyRelease: {
+ RedKey key = to_red_key_code(event.xkey.keycode);
+ if (key != REDKEY_KOREAN_HANGUL && key != REDKEY_KOREAN_HANGUL_HANJA) {
+ red_window->get_listener().on_key_release(key);
+ }
+ break;
+ }
+ case ButtonPress: {
+ unsigned int state = to_red_buttons_state(event.xbutton.state);
+ RedButton button = to_red_button(event.xbutton.button, state, true);
+ if (button == REDC_MOUSE_INVALID_BUTTON) {
+ DBG(0, "ButtonPress: invalid button %u", event.xbutton.button);
+ break;
+ }
+ red_window->get_listener().on_button_press(button, state);
+ break;
+ }
+ case ButtonRelease: {
+ unsigned int state = to_red_buttons_state(event.xbutton.state);
+ RedButton button = to_red_button(event.xbutton.button, state, false);
+ if (button == REDC_MOUSE_INVALID_BUTTON) {
+ DBG(0, "ButtonRelease: invalid button %u", event.xbutton.button);
+ break;
+ }
+ red_window->get_listener().on_button_release(button, state);
+ break;
+ }
+ case Expose: {
+ Point origin;
+ Rect area;
+
+ origin = red_window->get_origin();
+ area.left = event.xexpose.x - origin.x;
+ area.right = area.left + event.xexpose.width;
+ area.top = event.xexpose.y - origin.y;
+ area.bottom = area.top + event.xexpose.height;
+ red_window->get_listener().on_exposed_rect(area);
+ break;
+ }
+ case FocusIn:
+ if (!red_window->_ignore_foucs) {
+ red_window->on_focus_in();
+ } else {
+ red_window->_shadow_foucs_state = true;
+ }
+ break;
+ case FocusOut:
+ if (! red_window->_ignore_foucs) {
+ red_window->on_focus_out();
+ } else {
+ red_window->_shadow_foucs_state = false;
+ }
+ break;
+ case ConfigureNotify:
+ break;
+ case ClientMessage:
+ if (event.xclient.message_type == wm_protocol_atom) {
+ ASSERT(event.xclient.format == 32);
+ if (event.xclient.data.l[0] == wm_delete_window_atom) {
+ DBG(0, "wm_delete_window");
+ Platform::send_quit_request();
+ }
+ }
+ break;
+ case DestroyNotify:
+ break;
+ case MapNotify:
+ red_window->set_visibale(true);
+ break;
+ case UnmapNotify:
+ red_window->set_visibale(false);
+ break;
+ case EnterNotify:
+ red_window->on_pointer_enter();
+ break;
+ case LeaveNotify:
+ red_window->on_pointer_leave();
+ break;
+ }
+}
+
+void RedWindow_p::sync()
+{
+ XSync(x_display, False);
+ XEvent event;
+ while (XCheckWindowEvent(x_display, _win, ~long(0), &event)) {
+ win_proc(event);
+ }
+}
+
+void RedWindow_p::wait_for_reparent()
+{
+ XEvent event;
+ for (int i = 0; i < 50; i++) {
+ if (XCheckTypedWindowEvent(x_display, _win, ReparentNotify, &event)) {
+ return;
+ }
+ usleep(20 * 1000);
+ XSync(x_display, False);
+ }
+ DBG(0, "failed");
+}
+
+void RedWindow_p::wait_for_map()
+{
+ bool wait_parent = _expect_parent;
+ while (!_visibale) {
+ XEvent event;
+ XWindowEvent(x_display, _win, ~0, &event);
+ switch (event.type) {
+ case ReparentNotify:
+ wait_parent = false;
+ break;
+ case MapNotify:
+ _visibale = true;
+ break;
+ default:
+ //todo: post state messages to app message queue instead of
+ // calling win_proc
+ win_proc(event);
+ }
+ }
+
+ if (wait_parent) {
+ wait_for_reparent();
+ }
+}
+
+void RedWindow_p::wait_for_unmap()
+{
+ bool wait_parent = _expect_parent;
+ while (_visibale) {
+ XEvent event;
+ XWindowEvent(x_display, _win, ~0, &event);
+ switch (event.type) {
+ case ReparentNotify:
+ wait_parent = false;
+ break;
+ case UnmapNotify:
+ _visibale = false;
+ break;
+ //default:
+ // win_proc(event);
+ }
+ }
+
+ if (wait_parent) {
+ wait_for_reparent();
+ }
+}
+
+void RedWindow_p::set_glx(int width, int height)
+{
+ if (_glcont_copy) {
+ XSync(x_display, False);
+ glXMakeCurrent(x_display, _win, _glcont_copy);
+ //glDrawBuffer(GL_FRONT);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluOrtho2D(0, width, 0, height);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+ glColor3f(1, 1, 1);
+ glEnable(GL_TEXTURE_2D);
+ GLC_ERROR_TEST_FINISH;
+ }
+}
+
+void RedWindow_p::set_minmax(PixelsSource_p& pix_source, int width, int height)
+{
+ //todo: auto res
+ XSizeHints* size_hints = XAllocSizeHints();
+ ASSERT(size_hints);
+ size_hints->flags = PMinSize | PMaxSize;
+ size_hints->min_width = size_hints->max_width = width;
+ size_hints->min_height = size_hints->max_height = height;
+ XSetWMNormalHints(x_display, _win, size_hints);
+ XFree(size_hints);
+ pix_source.x_drawable.height = height;
+ pix_source.x_drawable.width = width;
+}
+
+Cursor RedWindow_p::create_invisible_cursor(Window window)
+{
+ XColor color;
+ char data[1] = {0};
+ Window root_window = RootWindow(x_display, DefaultScreen(x_display));
+ Pixmap blank = XCreateBitmapFromData(x_display, root_window, data, 1, 1);
+ Cursor cursor = XCreatePixmapCursor(x_display, blank, blank, &color, &color, 0, 0);
+ XFreePixmap(x_display, blank);
+ return cursor;
+}
+
+RedWindow_p::RedWindow_p()
+ : _win (None)
+ , _glcont_copy (NULL)
+ , _icon (NULL)
+ , _ignore_foucs (false)
+{
+}
+
+void RedWindow_p::destroy(PixelsSource_p& pix_source)
+{
+ if (_win == None) {
+ return;
+ }
+ XPlatform::cleare_win_proc(_win);
+ XSelectInput(x_display, _win, 0);
+ XSync(x_display, False);
+ XEvent event;
+ while (XCheckWindowEvent(x_display, _win, ~long(0), &event));
+ Window window = _win;
+ _win = None;
+ XFreeCursor(x_display, _invisible_cursor);
+ _invisible_cursor = None;
+ XDeleteContext(x_display, window, user_data_context);
+ if (_glcont_copy) {
+ glXDestroyContext(x_display, _glcont_copy);
+ _glcont_copy = NULL;
+ }
+ XDestroyWindow(x_display, window);
+ XFreeGC(x_display, pix_source.x_drawable.gc);
+ pix_source.x_drawable.gc = NULL;
+ pix_source.x_drawable.drawable = None;
+ if (_icon) {
+ _icon->unref();
+ }
+}
+
+void RedWindow_p::create(RedWindow& red_window, PixelsSource_p& pix_source, int x, int y,
+ unsigned int width, unsigned int height, int in_screen)
+{
+ Window window = None;
+ Cursor cursor = None;
+ GC gc = NULL;
+
+ Window root_window = RootWindow(x_display, in_screen);
+ XSetWindowAttributes win_attributes;
+
+ unsigned long mask = CWBorderPixel | CWEventMask;
+ win_attributes.border_pixel = 1;
+ win_attributes.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
+ KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | PointerMotionMask | FocusChangeMask |
+ EnterWindowMask | LeaveWindowMask;
+
+ Colormap colormap;
+
+ colormap = XCreateColormap(x_display, root_window, XPlatform::get_vinfo()[in_screen]->visual,
+ AllocNone);
+ win_attributes.colormap = colormap;
+ mask |= CWColormap;
+ window = XCreateWindow(x_display, root_window, x, y,
+ width, height, 0, XPlatform::get_vinfo()[in_screen]->depth,
+ InputOutput, XPlatform::get_vinfo()[in_screen]->visual, mask,
+ &win_attributes);
+ XFreeColormap(x_display, colormap);
+
+ if (!window) {
+ THROW("create X window failed");
+ }
+
+ try {
+ if (XSaveContext(x_display, window, user_data_context, (XPointer)&red_window)) {
+ THROW("set win usr data failed");
+ }
+
+ XSetWMProtocols(x_display, window, &wm_delete_window_atom, 1);
+ XGCValues gc_vals;
+ if (!(gc = XCreateGC(x_display, window, 0, &gc_vals))) {
+ THROW("create gc failed");
+ }
+ if (!(cursor = create_invisible_cursor(window))) {
+ THROW("create invisible cursor failed");
+ }
+
+ XPlatform::set_win_proc(window, win_proc);
+ } catch (...) {
+ if (gc) {
+ XFreeGC(x_display, gc);
+ }
+
+ XDeleteContext(x_display, window, user_data_context);
+ XDestroyWindow(x_display, window);
+ if (cursor != None) {
+ XFreeCursor(x_display, cursor);
+ }
+
+ throw;
+ }
+ _screen = in_screen;
+ _win = window;
+ _invisible_cursor = cursor;
+ _show_pos.x = x;
+ _show_pos.y = y;
+ _visibale = false;
+ _expect_parent = false;
+ pix_source.type = PIXELS_SOURCE_TYPE_X_DRAWABLE;
+ pix_source.x_drawable.drawable = window;
+ pix_source.x_drawable.gc = gc;
+ set_minmax(pix_source, width, height);
+ sync();
+}
+
+void RedWindow_p::migrate(RedWindow& red_window, PixelsSource_p& pix_source, int to_screen)
+{
+ if (to_screen == _screen) {
+ return;
+ }
+ XWindowAttributes attrib;
+ if (!XGetWindowAttributes(x_display, _win, &attrib)) {
+ THROW("get attributes failed");
+ }
+ XTextProperty text_pro;
+ bool valid_title = XGetWMName(x_display, _win, &text_pro) && text_pro.value;
+ destroy(pix_source);
+ create(red_window, pix_source, _show_pos.x, _show_pos.y, attrib.width, attrib.height,
+ to_screen);
+ if (valid_title) {
+ XSetWMName(x_display, _win, &text_pro);
+ XFree(text_pro.value); //???
+ }
+ if (_icon) {
+ AutoRef<Icon> red(_icon->ref());
+ red_window.set_icon(_icon);
+ }
+}
+
+void RedWindow_p::move_to_current_desktop()
+{
+ Window root = RootWindow(x_display, _screen);
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long bytes_after_return;
+ unsigned long nitems_return;
+ unsigned char *prop_return;
+ long desktop = ~long(0);
+
+ if (XGetWindowProperty(x_display, root, wm_current_desktop, 0, 1, False, AnyPropertyType,
+ &actual_type_return, &actual_format_return, &nitems_return,
+ &bytes_after_return, &prop_return) == Success &&
+ actual_type_return != None && actual_format_return == 32) {
+ desktop = *(uint32_t *)prop_return;
+ } else {
+ DBG(0, "get current desktop failed");
+ }
+
+ XEvent xevent;
+ xevent.type = ClientMessage;
+ xevent.xclient.window = _win;
+ xevent.xclient.message_type = wm_desktop;
+ xevent.xclient.format = 32;
+ xevent.xclient.data.l[0] = desktop;
+ xevent.xclient.data.l[1] = 0;
+ xevent.xclient.data.l[2] = 0;
+ xevent.xclient.data.l[3] = 0;
+ xevent.xclient.data.l[4] = 0;
+ if (!XSendEvent(x_display, root, False, SubstructureNotifyMask | SubstructureRedirectMask,
+ &xevent)) {
+ DBG(0, "failed");
+ }
+}
+
+RedWindow::RedWindow(RedWindow::Listener& listener, int screen)
+ : _listener (listener)
+ , _type (TYPE_NORMAL)
+ , _local_cursor (NULL)
+ , _cursor_visible (true)
+ , _focused (false)
+ , _pointer_in_window (false)
+ , _key_interception (false)
+ , _menu (NULL)
+{
+ ASSERT(x_display);
+ create(*this, *(PixelsSource_p*)get_opaque(), 0, 0, 200, 200,
+ (screen == DEFAULT_SCREEN) ? DefaultScreen(x_display) : screen);
+}
+
+RedWindow::~RedWindow()
+{
+ destroy(*(PixelsSource_p*)get_opaque());
+ if (_local_cursor) {
+ _local_cursor->unref();
+ }
+}
+
+void RedWindow::set_title(std::wstring& title)
+{
+ XTextProperty text_prop;
+ wchar_t *name = const_cast<wchar_t *>(title.c_str());
+ int r;
+ if (_win) {
+ r = XwcTextListToTextProperty(x_display, &name, 1, XStringStyle, &text_prop);
+ if (r >= 0) {
+ XSetWMName(x_display, _win, &text_prop);
+ XFree(text_prop.value);
+ } else {
+ LOG_WARN("XwcTextListToTextProperty Error %d", r);
+ }
+ }
+}
+
+void RedWindow::set_icon(Icon* icon)
+{
+ if (_icon) {
+ _icon->unref();
+ _icon = NULL;
+ }
+ if (!icon) {
+ return;
+ }
+ _icon = icon->ref();
+
+ XWMHints* wm_hints;
+ if (_win == None || !(wm_hints = XAllocWMHints())) {
+ return;
+ }
+
+ try {
+ XIcon* xicon = (XIcon*)icon;
+ xicon->get_pixmaps(_screen, wm_hints->icon_pixmap, wm_hints->icon_mask);
+ wm_hints->flags = IconPixmapHint | IconMaskHint;
+ XSetWMHints(x_display, _win, wm_hints);
+ } catch (...) {
+ }
+ XFree(wm_hints);
+}
+
+static XErrorHandler old_error_handler = NULL;
+static unsigned char x_error = Success;
+
+static int x_error_handler(Display* display, XErrorEvent* error_event)
+{
+ x_error = error_event->error_code;
+ if (error_event->error_code == BadWindow) {
+ return 0;
+ }
+ ASSERT(old_error_handler);
+ XSetErrorHandler(old_error_handler);
+ old_error_handler(display, error_event);
+ old_error_handler = NULL;
+ return 0;
+}
+
+class AutoXErrorHandler {
+public:
+ AutoXErrorHandler()
+ {
+ ASSERT(old_error_handler == NULL);
+ XSync(x_display, False);
+ x_error = Success;
+ old_error_handler = XSetErrorHandler(x_error_handler);
+ }
+
+ ~AutoXErrorHandler()
+ {
+ if (old_error_handler) {
+ XSetErrorHandler(old_error_handler);
+ old_error_handler = NULL;
+ }
+ }
+};
+
+static Window get_window_for_reposition(Window window)
+{
+ for (;;) {
+ Window root;
+ Window parent;
+ Window* childrens;
+ unsigned int num_childrens;
+
+ if (!XQueryTree(x_display, window, &root, &parent, &childrens, &num_childrens)) {
+ return None;
+ }
+
+ if (childrens) {
+ XFree(childrens);
+ }
+
+ if (parent == root) {
+ break;
+ }
+ window = parent;
+ }
+ return window;
+}
+
+void RedWindow::raise()
+{
+ AutoXErrorHandler auto_error_handler;
+ int raise_retries = RAISE_RETRIES;
+ for (;; --raise_retries) {
+ Window window = get_window_for_reposition(_win);
+ if (window != None) {
+ x_error = Success;
+ XRaiseWindow(x_display, window);
+ if (x_error == Success) {
+ break;
+ }
+ if (x_error != BadWindow) {
+ THROW("XRaiseWindow failed");
+ }
+ }
+
+ if (!raise_retries) {
+ THROW("failed");
+ }
+ usleep(X_RETRY_DELAY_MICRO);
+ }
+ if (raise_retries < RAISE_RETRIES) {
+ DBG(0, "retries %d", (RAISE_RETRIES - raise_retries));
+ }
+ sync();
+}
+
+void RedWindow::position_after(RedWindow *after)
+{
+ if (!after || after->_screen != _screen) {
+ raise();
+ return;
+ }
+
+ AutoXErrorHandler auto_error_handler;
+ int position_retries = Z_POSITION_RETRIES;
+ for (;; --position_retries) {
+ Window sibling = get_window_for_reposition(after->get_window());
+ Window self = get_window_for_reposition(_win);
+ if (sibling != None && self != None) {
+ XWindowChanges changes;
+ changes.sibling = sibling;
+ changes.stack_mode = Below;
+ x_error = Success;
+ XConfigureWindow(x_display, self, CWSibling | CWStackMode, &changes);
+ if (x_error == Success) {
+ break;
+ }
+ if (x_error != BadWindow) {
+ THROW("XConfigureWindow failed");
+ }
+ }
+
+ if (!position_retries) {
+ THROW("failed");
+ }
+ usleep(X_RETRY_DELAY_MICRO);
+ }
+ if (position_retries < Z_POSITION_RETRIES) {
+ DBG(0, "retries %d", (Z_POSITION_RETRIES - position_retries));
+ }
+}
+
+void RedWindow::show(int screen_id)
+{
+ if (_visibale) {
+ return;
+ }
+
+ bool wait_parent;
+
+ if (screen_id != _screen) {
+ _listener.pre_migrate();
+ migrate(*this, *(PixelsSource_p*)get_opaque(), screen_id);
+ _listener.post_migrate();
+ }
+
+ if (_type == TYPE_FULLSCREEN) {
+ Atom state[2];
+ state[0] = wm_state_above;
+ state[1] = wm_state_fullscreen;
+ XChangeProperty(x_display, _win, wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)state, 2);
+ wait_parent = false;
+ } else {
+ XDeleteProperty(x_display, _win, wm_state);
+ wait_parent = true;
+ }
+ XMapWindow(x_display, _win);
+ move_to_current_desktop();
+ _expect_parent = wait_parent;
+ wait_for_map();
+ move(_show_pos.x, _show_pos.y);
+}
+
+static bool get_prop_32(Window win, Atom prop, uint32_t &val)
+{
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long bytes_after_return;
+ unsigned long nitems_return;
+ unsigned char *prop_return;
+
+ if (XGetWindowProperty(x_display, win, prop, 0, 1, False, AnyPropertyType,
+ &actual_type_return, &actual_format_return,
+ &nitems_return, &bytes_after_return, &prop_return) == Success &&
+ nitems_return == 1 &&
+ actual_type_return != None &&
+ actual_format_return == 32) {
+ val = *(uint32_t *)prop_return;
+ return true;
+ }
+ return false;
+}
+
+void RedWindow::external_show()
+{
+ Atom atom;
+
+ DBG(0, "");
+ show(_screen);
+ raise();
+ activate();
+
+ atom = XInternAtom(x_display, "_NET_ACTIVE_WINDOW", true);
+ if (atom != None) {
+ Window root;
+ XEvent xev;
+ long eventmask;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.window = _win;
+ xev.xclient.message_type = atom;
+
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+
+ uint32_t user_time;
+ if (get_prop_32(_win, wm_user_time, user_time)) {
+ xev.xclient.data.l[1] = user_time;
+ } else {
+ xev.xclient.data.l[1] = 0;
+ }
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ root = RootWindow(x_display, _screen),
+ eventmask = SubstructureRedirectMask | SubstructureNotifyMask;
+
+ XSendEvent(x_display, root, False, eventmask, &xev);
+ }
+}
+
+void RedWindow::hide()
+{
+ if (!_visibale) {
+ return;
+ }
+ on_pointer_leave();
+ on_focus_out();
+ XUnmapWindow(x_display, _win);
+ _show_pos = get_position();
+ wait_for_unmap();
+ ASSERT(!_focused);
+ ASSERT(!_pointer_in_window);
+ _expect_parent = false;
+}
+
+static void send_expose(Window window, int width, int height)
+{
+ XExposeEvent event;
+ event.type = Expose;
+ event.display = x_display;
+ event.window = window;
+ event.x = 0;
+ event.y = 0;
+ event.width = width;
+ event.height = height;
+ event.count = 0;
+ XSendEvent(x_display, window, False, ExposureMask, (XEvent *)&event);
+}
+
+void RedWindow::move_and_resize(int x, int y, int width, int height)
+{
+ set_minmax(*(PixelsSource_p*)get_opaque(), width, height);
+ XMoveResizeWindow(x_display, _win, x, y, width, height);
+ _show_pos.x = x;
+ _show_pos.y = y;
+ if (_visibale) {
+ send_expose(_win, width, height);
+ }
+}
+
+void RedWindow::move(int x, int y)
+{
+ XMoveWindow(x_display, _win, x, y);
+ _show_pos.x = x;
+ _show_pos.y = y;
+}
+
+void RedWindow::resize(int width, int height)
+{
+ set_minmax(*(PixelsSource_p*)get_opaque(), width, height);
+ XResizeWindow(x_display, _win, width, height);
+ if (_visibale) {
+ send_expose(_win, width, height);
+ }
+}
+
+void RedWindow::activate()
+{
+ //todo: use _NET_ACTIVE_WINDOW
+ XSetInputFocus(x_display, _win, RevertToParent, CurrentTime);
+}
+
+void RedWindow::minimize()
+{
+ XIconifyWindow(x_display, _win, _screen);
+ sync();
+}
+
+static bool __get_position(Window window, Point& pos)
+{
+ pos.x = pos.y = 0;
+ for (;;) {
+ XWindowAttributes attrib;
+ Window root;
+ Window parent;
+ Window* childrens;
+ unsigned int num_childrens;
+
+ if (!XGetWindowAttributes(x_display, window, &attrib)) {
+ return false;
+ }
+ pos.x += attrib.x;
+ pos.y += attrib.y;
+
+ if (!XQueryTree(x_display, window, &root, &parent, &childrens, &num_childrens)) {
+ return false;
+ }
+
+ if (childrens) {
+ XFree(childrens);
+ }
+
+ if (parent == None) {
+ break;
+ }
+ window = parent;
+ }
+ return true;
+}
+
+Point RedWindow::get_position()
+{
+ Point pos;
+
+ AutoXErrorHandler auto_error_handler;
+ int get_position_retries = GET_POSITION_RETRIES;
+ for (;; --get_position_retries) {
+ if (__get_position(_win, pos)) {
+ break;
+ }
+ if (!get_position_retries) {
+ THROW("failed");
+ }
+ usleep(X_RETRY_DELAY_MICRO);
+ }
+
+ if (get_position_retries < GET_POSITION_RETRIES) {
+ DBG(0, "retries %d", (GET_POSITION_RETRIES - get_position_retries));
+ }
+ return pos;
+}
+
+void RedWindow::do_start_key_interception()
+{
+ // Working with KDE: XGrabKeyboard generate focusout and focusin events
+ // while we have the focus. This behavior trigger infinite recursive. for
+ // that reason we temporary disable focus event handling.
+
+ ASSERT(_focused);
+ _ignore_foucs = true;
+ _shadow_foucs_state = true;
+ XGrabKeyboard(x_display, _win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ sync();
+ _listener.on_start_key_interception();
+ _ignore_foucs = false;
+ if (!_shadow_foucs_state) {
+ on_focus_out();
+ }
+}
+
+void RedWindow::do_stop_key_interception()
+{
+ XUngrabKeyboard(x_display, CurrentTime);
+ sync();
+ _listener.on_stop_key_interception();
+}
+
+void RedWindow::start_key_interception()
+{
+ if (_key_interception) {
+ return;
+ }
+ _key_interception = true;
+ if (_pointer_in_window && _focused) {
+ do_start_key_interception();
+ }
+}
+
+void RedWindow::stop_key_interception()
+{
+ if (!_key_interception) {
+ return;
+ }
+ _key_interception = false;
+ if (_focused && _pointer_in_window) {
+ do_stop_key_interception();
+ }
+}
+
+void RedWindow::set_cursor(LocalCursor* local_cursor)
+{
+ ASSERT(local_cursor);
+ if (_local_cursor) {
+ _local_cursor->unref();
+ }
+ _local_cursor = local_cursor->ref();
+ _local_cursor->set(_win);
+ _cursor_visible = true;
+}
+
+void RedWindow::show_cursor()
+{
+ if (!_cursor_visible) {
+ if (_local_cursor) {
+ _local_cursor->set(_win);
+ }
+ _cursor_visible = true;
+ }
+}
+
+void RedWindow::hide_cursor()
+{
+ if (_cursor_visible) {
+ XDefineCursor(x_display, _win, _invisible_cursor);
+ _cursor_visible = false;
+ }
+}
+
+void RedWindow::release_mouse()
+{
+ XUngrabPointer(x_display, CurrentTime);
+ sync();
+}
+
+void RedWindow::cupture_mouse()
+{
+ int grab_retries = MOUSE_GRAB_RETRIES;
+ XSync(x_display, False);
+ for (;; --grab_retries) {
+ int result = XGrabPointer(x_display, _win, True, 0,
+ GrabModeAsync, GrabModeAsync,
+ _win, None, CurrentTime);
+ if (result == GrabSuccess) {
+ break;
+ }
+
+ if (!grab_retries) {
+ THROW("grab pointer failed (%d)", result);
+ }
+ usleep(X_RETRY_DELAY_MICRO);
+ DBG(0, "grab failed result=%d", result);
+ }
+ sync();
+}
+
+void RedWindow::set_mouse_position(int x, int y)
+{
+ XWarpPointer(x_display, None, _win, 0, 0, 0, 0, x + get_origin().x, y + get_origin().y);
+}
+
+Point RedWindow::get_size()
+{
+ XWindowAttributes attrib;
+ XGetWindowAttributes(x_display, _win, &attrib);
+ Point size;
+ size.x = attrib.width;
+ size.y = attrib.height;
+ return size;
+}
+
+static void window_area_from_attributes(Rect& area, XWindowAttributes& attrib)
+{
+ area.left = attrib.x;
+ area.right = area.left + attrib.width;
+ area.top = attrib.y;
+ area.bottom = area.top + attrib.height;
+}
+
+#define FAIL_ON_BAD_WINDOW(error, format, ...) \
+ if (error) { \
+ if ((x_error) == BadWindow) { \
+ return NULL; \
+ } \
+ THROW(format, ## __VA_ARGS__); \
+ }
+
+
+static QRegion *get_visibale_region(Window window)
+{
+ QRegion* region = new QRegion;
+ region_init(region);
+ XWindowAttributes attrib;
+ if (!XGetWindowAttributes(x_display, window, &attrib)) {
+ return NULL;
+ }
+
+ if (attrib.map_state != IsViewable) {
+ DBG(0, "not viewable");
+ return region;
+ }
+
+ Rect window_area;
+ window_area_from_attributes(window_area, attrib);
+ window_area.right -= window_area.left;
+ window_area.bottom -= window_area.top;
+ window_area.top = window_area.left = 0;
+ region_add(region, &window_area);
+ Window prev = None;
+ Window root;
+ Window parent;
+ Window* childrens;
+ unsigned int num_childrens;
+
+ AutoXErrorHandler auto_error_handler;
+ for (;;) {
+ FAIL_ON_BAD_WINDOW(!XQueryTree(x_display, window, &root, &parent, &childrens,
+ &num_childrens),
+ "%s: query X tree failed", __FUNCTION__);
+ for (int i = num_childrens - 1; i >= 0 && childrens[i] != prev; i--) {
+ FAIL_ON_BAD_WINDOW(!XGetWindowAttributes(x_display, childrens[i], &attrib),
+ "%s: get win attributes failed", __FUNCTION__);
+
+ if (attrib.map_state == IsViewable) {
+ window_area_from_attributes(window_area, attrib);
+ window_area.left -= attrib.border_width;
+ window_area.right += attrib.border_width;
+ window_area.top -= attrib.border_width;
+ window_area.bottom += attrib.border_width;
+ region_remove(region, &window_area);
+ }
+ }
+
+ if (childrens) {
+ XFree(childrens);
+ }
+
+ FAIL_ON_BAD_WINDOW(!XGetWindowAttributes(x_display, window, &attrib),
+ "%s: get win attributes failed", __FUNCTION__);
+ window_area_from_attributes(window_area, attrib);
+ region_offset(region, window_area.left, window_area.top);
+
+ if (parent == None) {
+ break;
+ }
+
+ FAIL_ON_BAD_WINDOW(!XGetWindowAttributes(x_display, parent, &attrib),
+ "%s: get win attributes failed", __FUNCTION__);
+ window_area_from_attributes(window_area, attrib);
+ window_area.right -= window_area.left;
+ window_area.bottom -= window_area.top;
+ window_area.top = window_area.left = 0;
+
+ QRegion parent_region;
+ region_init(&parent_region);
+ region_add(&parent_region, &window_area);
+ region_and(region, &parent_region);
+ region_destroy(&parent_region);
+
+ prev = window;
+ window = parent;
+ }
+
+ //todo: intersect with monitors
+ return region;
+}
+
+class Region_p {
+public:
+ Region_p(QRegion* region) : _region (region) {}
+ ~Region_p() { delete _region;}
+
+ void get_bbox(Rect& bbox) const
+ {
+ if (region_is_empty(_region)) {
+ bbox.left = bbox.right = bbox.top = bbox.bottom = 0;
+ } else {
+ bbox = _region->bbox;
+ }
+ }
+
+ bool contains_point(int x, int y) const
+ {
+ return region_contains_point(_region, x, y);
+ }
+
+private:
+ QRegion* _region;
+};
+
+bool RedWindow::get_mouse_anchor_point(Point& pt)
+{
+ QRegion* vis_region;
+ int vis_region_retries = GET_VIS_REGION_RETRIES;
+
+ while (!(vis_region = get_visibale_region(_win))) {
+ if (!vis_region_retries) {
+ THROW("get visibale region failed");
+ }
+ --vis_region_retries;
+ usleep(X_RETRY_DELAY_MICRO);
+ }
+
+ if (vis_region_retries < GET_VIS_REGION_RETRIES) {
+ DBG(0, "retries %d", (GET_VIS_REGION_RETRIES - vis_region_retries));
+ }
+
+ Region_p region(vis_region);
+ if (!find_anchor_point(region, pt)) {
+ return false;
+ }
+ Point position = get_position();
+ pt.x -= (position.x + get_origin().x);
+ pt.y -= (position.y + get_origin().y);
+ return true;
+}
+
+RedGlContext RedWindow::create_context_gl()
+{
+ if (XPlatform::get_fbconfig()[_screen]) {
+ return glXCreateContext(x_display, XPlatform::get_vinfo()[_screen], NULL, GL_TRUE);
+ }
+ return NULL;
+}
+
+RedPbuffer RedWindow::create_pbuff(int width, int height)
+{
+ GLXPbuffer pbuff;
+ GLXFBConfig** fb_config;
+
+ int pbuf_attr[] = { GLX_PRESERVED_CONTENTS, True,
+ GLX_PBUFFER_WIDTH, width,
+ GLX_PBUFFER_HEIGHT, height,
+ GLX_LARGEST_PBUFFER, False,
+ 0, 0 };
+
+ fb_config = XPlatform::get_fbconfig();
+ pbuff = glXCreatePbuffer(XPlatform::get_display(), fb_config[_screen][0],
+ pbuf_attr);
+
+ return pbuff;
+}
+
+void RedWindow::untouch_context()
+{
+ glXMakeCurrent(x_display, 0, 0);
+}
+
+int RedWindow::get_screen_num()
+{
+ return _screen;
+}
+
+void RedWindow::set_type_gl()
+{
+ PixelsSource_p *pix_source = (PixelsSource_p*)get_opaque();
+
+ pix_source->type = PIXELS_SOURCE_TYPE_GL_DRAWABLE;
+}
+
+void RedWindow::unset_type_gl()
+{
+ PixelsSource_p *pix_source = (PixelsSource_p*)get_opaque();
+
+ pix_source->type = PIXELS_SOURCE_TYPE_X_DRAWABLE;
+}
+
+void RedWindow::set_gl_context(RedGlContext context)
+{
+ PixelsSource_p *pix_source = (PixelsSource_p*)get_opaque();
+
+ pix_source->x_drawable.context = context;
+}
+
+void RedWindow::set_render_pbuff(RedPbuffer pbuff)
+{
+ PixelsSource_p *pix_source = (PixelsSource_p*)get_opaque();
+
+ pix_source->x_drawable.rendertype = RENDER_TYPE_PBUFF;
+ pix_source->x_drawable.pbuff = pbuff;
+}
+
+void RedWindow::set_render_fbo(GLuint fbo)
+{
+ PixelsSource_p *pix_source = (PixelsSource_p*)get_opaque();
+
+ pix_source->x_drawable.rendertype = RENDER_TYPE_FBO;
+ pix_source->x_drawable.fbo = fbo;
+}
+
+void RedWindow::on_focus_in()
+{
+ if (_focused) {
+ return;
+ }
+ _focused = true;
+ if (_key_interception && _pointer_in_window) {
+ do_start_key_interception();
+ }
+ XPlatform::on_focus_in();
+ get_listener().on_activate();
+}
+
+void RedWindow::on_focus_out()
+{
+ if (!_focused) {
+ return;
+ }
+ _focused = false;
+ do_stop_key_interception();
+ XPlatform::on_focus_out();
+ get_listener().on_deactivate();
+}
+
+void RedWindow::on_pointer_enter()
+{
+ if (_pointer_in_window) {
+ return;
+ }
+ _pointer_in_window = true;
+ _listener.on_pointer_enter();
+ if (_focused && _key_interception) {
+ do_start_key_interception();
+ }
+}
+
+void RedWindow::on_pointer_leave()
+{
+ if (!_pointer_in_window) {
+ return;
+ }
+ _pointer_in_window = false;
+ _listener.on_pointer_leave();
+ if (_focused && _key_interception) {
+ do_stop_key_interception();
+ }
+}
+
+void RedWindow::set_menu(Menu* menu)
+{
+}
+
+void RedWindow::init()
+{
+ x_display = XPlatform::get_display();
+ ASSERT(x_display);
+ user_data_context = XUniqueContext();
+
+ wm_protocol_atom = XInternAtom(x_display, "WM_PROTOCOLS", False);
+ wm_delete_window_atom = XInternAtom(x_display, "WM_DELETE_WINDOW", False);
+
+ wm_desktop = XInternAtom(x_display, "_NET_WM_DESKTOP", False);
+ wm_current_desktop = XInternAtom(x_display, "_NET_CURRENT_DESKTOP", False);
+
+ wm_state = XInternAtom(x_display, "_NET_WM_STATE", False);
+ wm_state_above = XInternAtom(x_display, "_NET_WM_STATE_ABOVE", False);
+ wm_state_fullscreen = XInternAtom(x_display, "_NET_WM_STATE_FULLSCREEN", False);
+
+ wm_user_time = XInternAtom(x_display, "_NET_WM_USER_TIME", False);
+
+#ifdef USE_X11_KEYCODE
+ init_key_map();
+#else
+ init_key_table_0xff();
+ init_key_table_0x00();
+ init_key_table_0xfe();
+#endif
+}
+