mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
2049 lines
62 KiB
C++
2049 lines
62 KiB
C++
/*
|
|
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 XIC x_input_context = NULL;
|
|
|
|
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;
|
|
|
|
static RedWindow* focus_window;
|
|
static unsigned long focus_serial = 0;
|
|
|
|
#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::handle_key_press_event(RedWindow& window, XKeyEvent* event)
|
|
{
|
|
static int buf_size = 0;
|
|
static char* utf8_buf = NULL;
|
|
static wchar_t* utf32_buf = NULL;
|
|
|
|
KeySym key_sym;
|
|
Status status;
|
|
int len;
|
|
|
|
window.get_listener().on_key_press(to_red_key_code(event->keycode));
|
|
for (;;) {
|
|
len = XwcLookupString(x_input_context, event, utf32_buf, buf_size, &key_sym, &status);
|
|
if (status != XBufferOverflow) {
|
|
break;
|
|
}
|
|
|
|
free(utf32_buf);
|
|
free(utf32_buf);
|
|
utf8_buf = new char[len];
|
|
utf32_buf = new wchar_t[len];
|
|
buf_size = len;
|
|
}
|
|
|
|
switch (status) {
|
|
case XLookupChars:
|
|
case XLookupBoth: {
|
|
uint32_t* now = (uint32_t*)utf32_buf;
|
|
uint32_t* end = now + len;
|
|
|
|
for (; now < end; now++) {
|
|
window.get_listener().on_char(*now);
|
|
}
|
|
break;
|
|
}
|
|
case XLookupNone:
|
|
case XLookupKeySym:
|
|
break;
|
|
default:
|
|
THROW("unexpected status %d", status);
|
|
}
|
|
}
|
|
|
|
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_pointer_motion(event.xmotion.x - origin.x,
|
|
event.xmotion.y - origin.y,
|
|
to_red_buttons_state(event.xmotion.state));
|
|
}
|
|
break;
|
|
}
|
|
case KeyPress:
|
|
red_window->handle_key_press_event(*red_window, &event.xkey);
|
|
break;
|
|
case KeyRelease: {
|
|
RedKey key = to_red_key_code(event.xkey.keycode);
|
|
XEvent next_event;
|
|
if (XCheckWindowEvent(x_display, red_window->_win, ~long(0), &next_event)) {
|
|
XPutBackEvent(x_display, &next_event);
|
|
if ((next_event.type == KeyPress) &&
|
|
(event.xkey.keycode == next_event.xkey.keycode) &&
|
|
(event.xkey.time == next_event.xkey.time)) {
|
|
break;
|
|
}
|
|
}
|
|
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_mouse_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_mouse_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 (event.xany.serial < focus_serial) {
|
|
DBG(0, "Ignored FocusIn win=%p (serial=%d, Last foucs serial=%d)",
|
|
red_window, event.xany.serial, focus_serial);
|
|
break;
|
|
}
|
|
|
|
if (!red_window->_ignore_foucs) {
|
|
RedWindow* prev_focus_window = focus_window;
|
|
focus_window = red_window;
|
|
focus_serial = event.xany.serial;
|
|
if (prev_focus_window && (red_window != prev_focus_window)) {
|
|
prev_focus_window->on_focus_out();
|
|
}
|
|
red_window->on_focus_in();
|
|
} else {
|
|
red_window->_shadow_foucs_state = true;
|
|
memcpy(&red_window->_shadow_focus_event, &event, sizeof(XEvent));
|
|
}
|
|
break;
|
|
case FocusOut:
|
|
if (event.xany.serial <= focus_serial) {
|
|
DBG(0, "Ignored FocusOut win=%p (serial=%d, Last foucs serial=%d)",
|
|
red_window, event.xany.serial, focus_serial);
|
|
break;
|
|
}
|
|
|
|
if (!red_window->_ignore_foucs) {
|
|
focus_serial = event.xany.serial;
|
|
if (red_window != focus_window) {
|
|
break;
|
|
}
|
|
focus_window = NULL;
|
|
red_window->on_focus_out();
|
|
} else {
|
|
red_window->_shadow_foucs_state = false;
|
|
memcpy(&red_window->_shadow_focus_event, &event, sizeof(XEvent));
|
|
}
|
|
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:
|
|
if (!red_window->_ignore_pointer) {
|
|
Point origin = red_window->get_origin();
|
|
red_window->on_pointer_enter(event.xcrossing.x - origin.x, event.xcrossing.y - origin.y,
|
|
to_red_buttons_state(event.xmotion.state));
|
|
} else {
|
|
red_window->_shadow_pointer_state = true;
|
|
memcpy(&red_window->_shadow_pointer_event, &event, sizeof(XEvent));
|
|
}
|
|
break;
|
|
case LeaveNotify:
|
|
if (!red_window->_ignore_pointer) {
|
|
red_window->on_pointer_leave();
|
|
} else {
|
|
red_window->_shadow_pointer_state = false;
|
|
memcpy(&red_window->_shadow_pointer_event, &event, sizeof(XEvent));
|
|
}
|
|
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)
|
|
, _ignore_pointer (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);
|
|
XFreeColormap(x_display, _colormap);
|
|
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 = 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);
|
|
|
|
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)
|
|
, _trace_key_interception (false)
|
|
, _key_interception_on (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. Same happens
|
|
// LeaveNotify and EnterNotify.
|
|
|
|
ASSERT(_focused);
|
|
_ignore_foucs = true;
|
|
_ignore_pointer = true;
|
|
_shadow_foucs_state = true;
|
|
_shadow_pointer_state = true;
|
|
_shadow_focus_event.xany.serial = 0;
|
|
XGrabKeyboard(x_display, _win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
sync();
|
|
_listener.on_start_key_interception();
|
|
_ignore_foucs = false;
|
|
_ignore_pointer = false;
|
|
_key_interception_on = true;
|
|
if (!_shadow_foucs_state) {
|
|
DBG(0, "put back shadowed focus out");
|
|
XPutBackEvent(x_display, &_shadow_focus_event);
|
|
} else if (_shadow_focus_event.xany.serial > 0) {
|
|
ASSERT(focus_window == this);
|
|
focus_serial = _shadow_focus_event.xany.serial;
|
|
}
|
|
|
|
if (!_shadow_pointer_state) {
|
|
DBG(0, "put back shadowed pointer leave");
|
|
XPutBackEvent(x_display, &_shadow_pointer_event);
|
|
}
|
|
}
|
|
|
|
void RedWindow::do_stop_key_interception()
|
|
{
|
|
_ignore_foucs = true;
|
|
_ignore_pointer = true;
|
|
_shadow_foucs_state = _focused;
|
|
_shadow_pointer_state = _pointer_in_window;
|
|
_shadow_focus_event.xany.serial = 0;
|
|
XUngrabKeyboard(x_display, CurrentTime);
|
|
sync();
|
|
_key_interception_on = false;
|
|
_listener.on_stop_key_interception();
|
|
_ignore_foucs = false;
|
|
_ignore_pointer = false;
|
|
if (_shadow_foucs_state != _focused) {
|
|
DBG(0, "put back shadowed focus event");
|
|
XPutBackEvent(x_display, &_shadow_focus_event);
|
|
} else if (_shadow_focus_event.xany.serial > 0) {
|
|
focus_serial = _shadow_focus_event.xany.serial;
|
|
}
|
|
|
|
if (_shadow_pointer_state != _pointer_in_window) {
|
|
DBG(0, "put back shadowed pointer event");
|
|
XPutBackEvent(x_display, &_shadow_pointer_event);
|
|
}
|
|
}
|
|
|
|
void RedWindow::start_key_interception()
|
|
{
|
|
if (_trace_key_interception) {
|
|
return;
|
|
}
|
|
_trace_key_interception = true;
|
|
if (_pointer_in_window && _focused) {
|
|
do_start_key_interception();
|
|
}
|
|
}
|
|
|
|
void RedWindow::stop_key_interception()
|
|
{
|
|
if (!_trace_key_interception) {
|
|
return;
|
|
}
|
|
_trace_key_interception = false;
|
|
if (_key_interception_on) {
|
|
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;
|
|
XwcResetIC(x_input_context);
|
|
XPlatform::on_focus_in();
|
|
get_listener().on_activate();
|
|
if (_trace_key_interception && _pointer_in_window) {
|
|
do_start_key_interception();
|
|
}
|
|
}
|
|
|
|
void RedWindow::on_focus_out()
|
|
{
|
|
if (!_focused) {
|
|
return;
|
|
}
|
|
_focused = false;
|
|
if (_key_interception_on) {
|
|
do_stop_key_interception();
|
|
}
|
|
get_listener().on_deactivate();
|
|
XPlatform::on_focus_out();
|
|
}
|
|
|
|
void RedWindow::on_pointer_enter(int x, int y, unsigned int buttons_state)
|
|
{
|
|
if (_pointer_in_window) {
|
|
return;
|
|
}
|
|
_pointer_in_window = true;
|
|
_listener.on_pointer_enter(x, y, buttons_state);
|
|
if (_focused && _trace_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 (_key_interception_on) {
|
|
do_stop_key_interception();
|
|
}
|
|
}
|
|
|
|
void RedWindow::set_menu(Menu* menu)
|
|
{
|
|
}
|
|
|
|
void RedWindow::init()
|
|
{
|
|
x_display = XPlatform::get_display();
|
|
x_input_context = XPlatform::get_input_context();
|
|
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
|
|
}
|
|
|