diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-09-19 21:25:46 +0300 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-10-14 15:06:41 +0200 |
commit | c1b79eb035fa158fb2ac3bc8e559809611070016 (patch) | |
tree | 3348dd749a700dedf87c9b16fe8be77c62928df8 /client/x11/red_window.cpp | |
download | spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.gz spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.xz spice-c1b79eb035fa158fb2ac3bc8e559809611070016.zip |
fresh start
Diffstat (limited to 'client/x11/red_window.cpp')
-rw-r--r-- | client/x11/red_window.cpp | 1917 |
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 +} + |