09a0573e61
[CONFIGURATION] Replace my old sourceforge mail address by the new one.
836 lines
19 KiB
C
836 lines
19 KiB
C
/***************************************************************************
|
|
|
|
x11.c
|
|
|
|
(c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>
|
|
|
|
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, 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, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __X11_C
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "main.h"
|
|
#include "x11.h"
|
|
#include "c_x11.h"
|
|
#include "systray/systray.h"
|
|
|
|
#define MAX_WINDOW_PROP 16
|
|
|
|
Atom X11_atom_net_wm_state;
|
|
Atom X11_atom_net_wm_state_above;
|
|
Atom X11_atom_net_wm_state_below;
|
|
Atom X11_atom_net_wm_state_stays_on_top;
|
|
Atom X11_atom_net_wm_state_skip_taskbar;
|
|
Atom X11_atom_net_wm_window_type;
|
|
Atom X11_atom_net_wm_window_type_normal;
|
|
Atom X11_atom_net_wm_window_type_utility;
|
|
Atom X11_atom_net_wm_desktop;
|
|
Atom X11_atom_net_wm_user_time;
|
|
Atom X11_atom_net_current_desktop;
|
|
Atom X11_atom_net_workarea = None;
|
|
Atom X11_atom_motif_wm_hints = None;
|
|
Atom X11_atom_net_system_tray = None;
|
|
|
|
Atom X11_UTF8_STRING;
|
|
|
|
bool X11_ready = FALSE;
|
|
|
|
Display *X11_display = NULL;
|
|
#define _display X11_display
|
|
Window X11_root = 0;
|
|
#define _root X11_root
|
|
bool X11_event_filter_enabled = FALSE;
|
|
|
|
static bool _atom_init = FALSE;
|
|
|
|
static bool _has_test_extension = FALSE;
|
|
static bool _init_keycode = FALSE;
|
|
|
|
static int _min_keycode, _max_keycode, _keysyms_per_keycode;
|
|
static KeySym *_keycode_map = NULL;
|
|
|
|
static XModifierKeymap *_modifier_map = NULL;
|
|
static KeyCode *_shift_keycode = NULL;
|
|
static KeyCode *_alt_gr_keycode = NULL;
|
|
|
|
typedef
|
|
struct {
|
|
int count;
|
|
Atom atoms[MAX_WINDOW_PROP];
|
|
bool changed;
|
|
}
|
|
X11_PROPERTY;
|
|
|
|
static X11_PROPERTY _window_prop;
|
|
static X11_PROPERTY _window_save[2];
|
|
|
|
static char *_property_value = NULL;
|
|
|
|
static GB_FUNCTION _x11_property_notify_func;
|
|
static GB_FUNCTION _x11_configure_notify_func;
|
|
|
|
static void init_atoms()
|
|
{
|
|
if (_atom_init)
|
|
return;
|
|
|
|
//fprintf(stderr, "init_atom: display = %p\n", _display);
|
|
X11_atom_net_current_desktop = XInternAtom(_display, "_NET_CURRENT_DESKTOP", True);
|
|
//fprintf(stderr, "init_atom: #1\n");
|
|
X11_atom_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", True);
|
|
X11_atom_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", True);
|
|
X11_atom_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", True);
|
|
X11_atom_net_wm_state_stays_on_top = XInternAtom(_display, "_NET_WM_STATE_STAYS_ON_TOP", True);
|
|
X11_atom_net_wm_state_skip_taskbar = XInternAtom(_display, "_NET_WM_STATE_SKIP_TASKBAR", True);
|
|
|
|
X11_atom_net_wm_desktop = XInternAtom(_display, "_NET_WM_DESKTOP", True);
|
|
X11_atom_net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", True);
|
|
X11_atom_net_wm_window_type_normal = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_NORMAL", True);
|
|
X11_atom_net_wm_window_type_utility = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_UTILITY", True);
|
|
X11_atom_net_wm_user_time = XInternAtom(_display, "_NET_WM_USER_TIME", True);
|
|
|
|
X11_UTF8_STRING = XInternAtom(X11_display, "UTF8_STRING", True);
|
|
|
|
_atom_init = TRUE;
|
|
}
|
|
|
|
#define PROPERTY_START_READ 1024
|
|
#define PROPERTY_NEXT_READ 1024
|
|
|
|
#if 0
|
|
// On 64 bits OS, format 32 are actually long, i.e. int padded to 64 bits!
|
|
#if OS_64BITS
|
|
if (format == 32)
|
|
{
|
|
int *p = (int *)_property_value;
|
|
for (i = 0; i < count; i++)
|
|
p[i] = *((long *)data + i);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
memcpy(_property_value, (char *)data, count * size);
|
|
}
|
|
#endif
|
|
|
|
static size_t sizeof_format(int format)
|
|
{
|
|
return format == 32 ? sizeof(long) : (format == 16 ? sizeof(short) : sizeof(char));
|
|
}
|
|
|
|
char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount)
|
|
{
|
|
uchar *data;
|
|
unsigned long count;
|
|
unsigned long after;
|
|
unsigned long offset;
|
|
int size, offset_size;
|
|
|
|
*pcount = 0;
|
|
|
|
if (XGetWindowProperty(_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t),
|
|
False, AnyPropertyType, type, format,
|
|
&count, &after, &data) != Success)
|
|
return NULL;
|
|
|
|
*pcount += count;
|
|
|
|
size = sizeof_format(*format);
|
|
offset_size = *format == 32 ? sizeof(int32_t) : ( *format == 16 ? sizeof(short) : 1 );
|
|
|
|
//fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld\n", *format, size, count, after);
|
|
|
|
GB.FreeString(&_property_value);
|
|
_property_value = GB.NewString((char *)data, count * size);
|
|
XFree(data);
|
|
|
|
offset = count * offset_size / sizeof(int32_t);
|
|
|
|
while (after)
|
|
{
|
|
//fprintf(stderr, "X11_get_property: offset = %ld read = %ld\n", offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t));
|
|
|
|
if (XGetWindowProperty(_display, wid, prop, offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t),
|
|
False, AnyPropertyType, type, format,
|
|
&count, &after, &data) != Success)
|
|
return NULL;
|
|
|
|
*pcount += count;
|
|
offset += count * offset_size / sizeof(int32_t);
|
|
|
|
//fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld next offset = %ld\n", *format, size, count, after, offset);
|
|
|
|
_property_value = GB.AddString(_property_value, (char *)data, count * size);
|
|
XFree(data);
|
|
}
|
|
|
|
return _property_value;
|
|
}
|
|
|
|
#if 0
|
|
static void get_property(Window wid, Atom prop, int maxlength, unsigned char **data, unsigned long *count)
|
|
{
|
|
Atom type;
|
|
int format;
|
|
unsigned long after;
|
|
|
|
XGetWindowProperty(_display, wid, prop, 0, maxlength / 4,
|
|
False, AnyPropertyType, &type, &format,
|
|
count, &after, data);
|
|
}
|
|
#endif
|
|
|
|
static char *get_property(Window wid, Atom prop, int *count)
|
|
{
|
|
Atom type;
|
|
int format;
|
|
|
|
return X11_get_property(wid, prop, &type, &format, count);
|
|
}
|
|
|
|
Atom X11_get_property_type(Window wid, Atom prop, int *format)
|
|
{
|
|
uchar *data = NULL;
|
|
unsigned long count;
|
|
unsigned long after;
|
|
Atom type;
|
|
|
|
if (XGetWindowProperty(X11_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t),
|
|
False, AnyPropertyType, &type, format,
|
|
&count, &after, &data) != Success)
|
|
return None;
|
|
|
|
XFree(data);
|
|
return type;
|
|
}
|
|
|
|
#if 0
|
|
#if OS_64BITS
|
|
long padded_data[count];
|
|
int i;
|
|
|
|
if (format == 32)
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
padded_data[i] = ((int *)data)[i];
|
|
}
|
|
data = padded_data;
|
|
#endif
|
|
#endif
|
|
|
|
void X11_set_property(Window wid, Atom prop, Atom type, int format, void *data, int count)
|
|
{
|
|
XChangeProperty(X11_display, wid, prop, type, format, PropModeReplace, (uchar *)data, count);
|
|
}
|
|
|
|
Atom X11_intern_atom(const char *name, bool create)
|
|
{
|
|
int val = atoi(name);
|
|
|
|
if (val)
|
|
return (Atom)val;
|
|
else
|
|
return XInternAtom(X11_display, name, !create);
|
|
}
|
|
|
|
static void load_window_state(Window win, Atom prop)
|
|
{
|
|
int length;
|
|
char *data;
|
|
|
|
_window_prop.count = 0;
|
|
_window_prop.changed = FALSE;
|
|
|
|
data = get_property(win, prop, &length);
|
|
|
|
if (length > MAX_WINDOW_PROP)
|
|
length = MAX_WINDOW_PROP;
|
|
|
|
_window_prop.count = length;
|
|
if (data)
|
|
memcpy(_window_prop.atoms, data, length * sizeof(Atom));
|
|
}
|
|
|
|
static void save_window_state(Window win, Atom prop)
|
|
{
|
|
if (_window_prop.changed)
|
|
{
|
|
//fprintf(stderr, "XChangeProperty: %ld %ld\n", (long)win, (long)prop);
|
|
XChangeProperty(_display, win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)_window_prop.atoms, _window_prop.count);
|
|
}
|
|
}
|
|
|
|
static bool has_window_state(Atom prop)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < _window_prop.count; i++)
|
|
{
|
|
if (_window_prop.atoms[i] == prop)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void set_window_state(Atom prop)
|
|
{
|
|
if (has_window_state(prop))
|
|
return;
|
|
|
|
if (_window_prop.count == MAX_WINDOW_PROP)
|
|
{
|
|
fprintf(stderr, "X11: set_window_state: Too many properties in window\n");
|
|
return;
|
|
}
|
|
|
|
_window_prop.atoms[_window_prop.count++] = prop;
|
|
_window_prop.changed = TRUE;
|
|
}
|
|
|
|
static void clear_window_state(Atom prop)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < _window_prop.count; i++)
|
|
{
|
|
if (_window_prop.atoms[i] == prop)
|
|
{
|
|
_window_prop.count--;
|
|
|
|
for (; i < _window_prop.count; i++)
|
|
_window_prop.atoms[i] = _window_prop.atoms[i + 1];
|
|
|
|
_window_prop.changed = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool X11_do_init()
|
|
{
|
|
int event_base, error_base, major_version, minor_version;
|
|
|
|
if (X11_ready)
|
|
return FALSE;
|
|
|
|
GB.Component.GetInfo("DISPLAY", POINTER(&_display));
|
|
|
|
_root = DefaultRootWindow(_display);
|
|
|
|
X11_ready = _display != NULL;
|
|
|
|
if (!X11_ready)
|
|
{
|
|
fprintf(stderr, "WARNING: X11_init() has failed\n");
|
|
return TRUE;
|
|
}
|
|
|
|
init_atoms();
|
|
|
|
_has_test_extension = XTestQueryExtension(_display, &event_base, &error_base, &major_version, &minor_version);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void X11_exit()
|
|
{
|
|
if (_keycode_map)
|
|
XFree(_keycode_map);
|
|
if (_modifier_map)
|
|
XFreeModifiermap(_modifier_map);
|
|
if (_property_value)
|
|
GB.FreeString(&_property_value);
|
|
}
|
|
|
|
void X11_send_client_message(Window dest, Window window, Atom message, char *data, int format, int count)
|
|
{
|
|
XEvent e;
|
|
int mask = (SubstructureRedirectMask | SubstructureNotifyMask);
|
|
|
|
//fprintf(stderr, "X11_send_client_message: dest = %ld window = %ld message = %ld format = %d count = %d\n", dest, window, message, format, count);
|
|
|
|
e.xclient.type = ClientMessage;
|
|
e.xclient.message_type = message;
|
|
e.xclient.display = X11_display;
|
|
e.xclient.window = window;
|
|
e.xclient.format = format;
|
|
|
|
memset(&e.xclient.data.l[0], 0, sizeof(long) * 5);
|
|
if (data)
|
|
{
|
|
count *= sizeof_format(format);
|
|
if (count > (sizeof(long) * 5))
|
|
count = sizeof(long) * 5;
|
|
memcpy(&e.xclient.data.l[0], data, count);
|
|
//fprintf(stderr, "%ld %ld %ld %ld %ld\n", e.xclient.data.l[0], e.xclient.data.l[1], e.xclient.data.l[2], e.xclient.data.l[3], e.xclient.data.l[4]);
|
|
}
|
|
|
|
XSendEvent(X11_display, dest, False, mask, &e);
|
|
XFlush(X11_display);
|
|
}
|
|
|
|
void X11_window_change_property(Window window, bool visible, Atom property, bool set)
|
|
{
|
|
XEvent e;
|
|
long mask = (SubstructureRedirectMask | SubstructureNotifyMask);
|
|
|
|
if (visible)
|
|
{
|
|
e.xclient.type = ClientMessage;
|
|
e.xclient.message_type = X11_atom_net_wm_state;
|
|
e.xclient.display = _display;
|
|
e.xclient.window = window;
|
|
e.xclient.format = 32;
|
|
e.xclient.data.l[0] = set ? 1 : 0;
|
|
e.xclient.data.l[1] = property;
|
|
e.xclient.data.l[2] = 0;
|
|
e.xclient.data.l[3] = 0;
|
|
e.xclient.data.l[4] = 0;
|
|
|
|
XSendEvent(_display, _root, False, mask, &e);
|
|
XFlush(X11_display);
|
|
}
|
|
else
|
|
{
|
|
load_window_state(window, X11_atom_net_wm_state);
|
|
|
|
if (set)
|
|
set_window_state(property);
|
|
else
|
|
clear_window_state(property);
|
|
|
|
save_window_state(window, X11_atom_net_wm_state);
|
|
}
|
|
}
|
|
|
|
|
|
bool X11_window_has_property(Window window, Atom property)
|
|
{
|
|
load_window_state(window, X11_atom_net_wm_state);
|
|
return has_window_state(property);
|
|
}
|
|
|
|
void X11_sync(void)
|
|
{
|
|
XSync(_display, False);
|
|
}
|
|
|
|
void X11_window_save_properties(Window window)
|
|
{
|
|
load_window_state(window, X11_atom_net_wm_state);
|
|
_window_save[0] = _window_prop;
|
|
//load_window_state(window, X11_atom_net_wm_window_type);
|
|
//_window_save[1] = _window_prop;
|
|
}
|
|
|
|
void X11_window_restore_properties(Window window)
|
|
{
|
|
_window_prop = _window_save[0];
|
|
save_window_state(window, X11_atom_net_wm_state);
|
|
//_window_prop = _window_save[1];
|
|
//save_window_state(window, X11_atom_net_wm_window_type);
|
|
}
|
|
|
|
#define SYSTEM_TRAY_REQUEST_DOCK 0
|
|
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
|
|
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
|
|
|
|
#define OPCODE "_NET_SYSTEM_TRAY_OPCODE"
|
|
|
|
void X11_window_dock(Window window)
|
|
{
|
|
Window xmanager=None;
|
|
XClientMessageEvent ev;
|
|
Atom OpCodeAtom;
|
|
Screen *xscreen;
|
|
char buf[256];
|
|
Atom selection_atom;
|
|
|
|
buf[0]=0;
|
|
|
|
xscreen = DefaultScreenOfDisplay(_display);
|
|
sprintf(buf,"_NET_SYSTEM_TRAY_S%d",XScreenNumberOfScreen(xscreen));
|
|
selection_atom = XInternAtom(_display, buf, 0);
|
|
|
|
XGrabServer(_display);
|
|
|
|
xmanager = XGetSelectionOwner(_display, selection_atom);
|
|
if (xmanager != None)
|
|
XSelectInput(_display, xmanager, StructureNotifyMask);
|
|
|
|
XUngrabServer(_display);
|
|
XFlush(_display);
|
|
|
|
/***********************************************
|
|
Dock Tray Icon
|
|
************************************************/
|
|
|
|
OpCodeAtom = XInternAtom(_display, OPCODE, 0);
|
|
|
|
ev.type = ClientMessage;
|
|
ev.window = xmanager;
|
|
ev.message_type = OpCodeAtom;
|
|
ev.format = 32;
|
|
ev.data.l[0] = 0;
|
|
ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
|
|
ev.data.l[2] = window;
|
|
ev.data.l[3] = 0;
|
|
ev.data.l[4] = 0;
|
|
|
|
XSendEvent(_display, xmanager, 0, NoEventMask, (XEvent *)&ev);
|
|
XSync(_display, 0);
|
|
}
|
|
|
|
void X11_window_startup(Window window, int x, int y, int w, int h)
|
|
{
|
|
XSizeHints s;
|
|
|
|
s.flags = USPosition | PPosition | USSize | PSize;
|
|
|
|
s.x = x;
|
|
s.y = y;
|
|
s.width = w;
|
|
s.height = h;
|
|
|
|
XSetWMNormalHints(_display, window, &s);
|
|
}
|
|
|
|
void X11_find_windows(Window **window_list, int *count)
|
|
{
|
|
static Atom _net_client_list = 0;
|
|
|
|
if (!_net_client_list)
|
|
_net_client_list = XInternAtom(_display, "_NET_CLIENT_LIST", True);
|
|
|
|
*window_list = (Window *)get_property(_root, _net_client_list, count);
|
|
}
|
|
|
|
void X11_get_window_title(Window window, char **result, int *length)
|
|
{
|
|
static Atom _net_wm_name = 0;
|
|
|
|
if (!_net_wm_name)
|
|
_net_wm_name = XInternAtom(_display, "_NET_WM_NAME", True);
|
|
|
|
*result = get_property(window, _net_wm_name, length);
|
|
}
|
|
|
|
void X11_get_window_class(Window window, char **result, int *length)
|
|
{
|
|
*result = get_property(window, XA_WM_CLASS, length);
|
|
}
|
|
|
|
void X11_get_window_role(Window window, char **result, int *length)
|
|
{
|
|
static Atom wm_window_role = (Atom)0;
|
|
|
|
if (!wm_window_role)
|
|
wm_window_role = XInternAtom(_display, "WM_WINDOW_ROLE", True);
|
|
|
|
*result = get_property(window, wm_window_role, length);
|
|
}
|
|
|
|
|
|
// Makes a tool window
|
|
|
|
void X11_set_window_tool(Window window, int tool, Window parent)
|
|
{
|
|
load_window_state(window, X11_atom_net_wm_window_type);
|
|
|
|
if (tool)
|
|
{
|
|
set_window_state(X11_atom_net_wm_window_type_utility);
|
|
clear_window_state(X11_atom_net_wm_window_type_normal);
|
|
if (parent)
|
|
XSetTransientForHint(_display, window, parent);
|
|
}
|
|
else
|
|
{
|
|
clear_window_state(X11_atom_net_wm_window_type_utility);
|
|
set_window_state(X11_atom_net_wm_window_type_normal);
|
|
}
|
|
|
|
save_window_state(window, X11_atom_net_wm_window_type);
|
|
}
|
|
|
|
int X11_get_window_tool(Window window)
|
|
{
|
|
load_window_state(window, X11_atom_net_wm_window_type);
|
|
return has_window_state(X11_atom_net_wm_window_type_utility);
|
|
}
|
|
|
|
|
|
// Set window desktop
|
|
|
|
void X11_window_set_desktop(Window window, bool visible, int desktop)
|
|
{
|
|
XEvent e;
|
|
long mask = (SubstructureRedirectMask | SubstructureNotifyMask);
|
|
|
|
if (visible)
|
|
{
|
|
e.xclient.type = ClientMessage;
|
|
e.xclient.message_type = X11_atom_net_wm_desktop;
|
|
e.xclient.display = _display;
|
|
e.xclient.window = window;
|
|
e.xclient.format = 32;
|
|
e.xclient.data.l[0] = desktop;
|
|
e.xclient.data.l[1] = 1;
|
|
e.xclient.data.l[2] = 0;
|
|
e.xclient.data.l[3] = 0;
|
|
e.xclient.data.l[4] = 0;
|
|
|
|
XSendEvent(_display, _root, False, mask, &e);
|
|
XFlush(_display);
|
|
}
|
|
else
|
|
{
|
|
XChangeProperty(_display, window, X11_atom_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace,
|
|
(unsigned char *)&desktop, 1);
|
|
XFlush(_display);
|
|
}
|
|
}
|
|
|
|
|
|
int X11_window_get_desktop(Window window)
|
|
{
|
|
int length;
|
|
char *data = NULL;
|
|
int desktop = 0;
|
|
|
|
data = get_property(window, X11_atom_net_wm_desktop, &length);
|
|
if (data)
|
|
desktop = *((int *)data);
|
|
|
|
return desktop;
|
|
}
|
|
|
|
int X11_get_current_desktop()
|
|
{
|
|
int length;
|
|
char *data;
|
|
int desktop = 0;
|
|
|
|
data = get_property(_root, X11_atom_net_current_desktop, &length);
|
|
if (data)
|
|
desktop = *((int *)data);
|
|
|
|
return desktop;
|
|
}
|
|
|
|
static void init_keycode()
|
|
{
|
|
int i, j; //, k;
|
|
KeyCode *pm, *p;
|
|
|
|
if (_init_keycode)
|
|
return;
|
|
|
|
XDisplayKeycodes(_display, &_min_keycode, &_max_keycode);
|
|
|
|
_keycode_map = XGetKeyboardMapping(_display, _min_keycode, _max_keycode - _min_keycode + 1, &_keysyms_per_keycode);
|
|
_modifier_map = XGetModifierMapping(_display);
|
|
|
|
p = _modifier_map->modifiermap;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
pm = p;
|
|
for (j = 0; j < _modifier_map->max_keypermod; j++)
|
|
{
|
|
//for (k = 0; k < 3; k++)
|
|
switch (XkbKeycodeToKeysym(_display, *p, 0, 0))
|
|
{
|
|
case XK_Shift_L: _shift_keycode = pm; break;
|
|
case XK_Mode_switch: _alt_gr_keycode = pm; break;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
//fprintf(stderr, "SHIFT: %d ALTGR: %d\n", _shift_keycode[0], _alt_gr_keycode[0]);
|
|
|
|
_init_keycode = TRUE;
|
|
}
|
|
|
|
static void send_modifiers(KeyCode *codes, bool press)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < _modifier_map->max_keypermod; i++)
|
|
{
|
|
if (codes[i])
|
|
XTestFakeKeyEvent(_display, codes[i], press, CurrentTime);
|
|
}
|
|
}
|
|
|
|
static void handle_modifier(KeyCode code, KeySym keysym, bool press)
|
|
{
|
|
KeySym *sym;
|
|
int i;
|
|
|
|
sym = &_keycode_map[(code - _min_keycode) * _keysyms_per_keycode];
|
|
|
|
for (i = 0; i < _keysyms_per_keycode; i++)
|
|
{
|
|
if (keysym == sym[i])
|
|
{
|
|
//fprintf(stderr, "0x%04X: %d\n", keysym, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(i)
|
|
{
|
|
case 1:
|
|
send_modifiers(_shift_keycode, press);
|
|
break;
|
|
case 2:
|
|
send_modifiers(_alt_gr_keycode, press);
|
|
break;
|
|
case 3:
|
|
send_modifiers(_shift_keycode, press);
|
|
send_modifiers(_alt_gr_keycode, press);
|
|
break;
|
|
//default:
|
|
// fprintf(stderr, "0x%04X: ?\n", keysym);
|
|
}
|
|
}
|
|
|
|
char *X11_send_key(char *key, bool press)
|
|
{
|
|
KeySym keysym;
|
|
KeyCode code;
|
|
|
|
if (!_has_test_extension)
|
|
return "No XTEST extension";
|
|
|
|
if (!_init_keycode)
|
|
init_keycode();
|
|
|
|
//fprintf(stderr, "X11_send_key: '%s' %d\n", key, XStringToKeysym(key));
|
|
|
|
if (strlen(key) == 1)
|
|
{
|
|
if (*key == '\n')
|
|
keysym = XK_Return;
|
|
else if (*key == '\t')
|
|
keysym = XK_Tab;
|
|
else if (*key >= ' ' || *key < 0)
|
|
keysym = (int)*key & 0xFF;
|
|
else
|
|
keysym = NoSymbol;
|
|
}
|
|
else
|
|
keysym = XStringToKeysym(key);
|
|
|
|
if (keysym == NoSymbol)
|
|
return "Unknown key";
|
|
|
|
code = XKeysymToKeycode(_display, keysym);
|
|
if (code)
|
|
{
|
|
if (press)
|
|
handle_modifier(code, keysym, TRUE);
|
|
|
|
XTestFakeKeyEvent(_display, code, press, CurrentTime);
|
|
|
|
if (press)
|
|
handle_modifier(code, keysym, FALSE);
|
|
}
|
|
|
|
usleep(1000);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void X11_enable_event_filter(bool enable)
|
|
{
|
|
static int count = 0;
|
|
|
|
void (*set_event_filter)(void *) = NULL;
|
|
|
|
if (enable)
|
|
count++;
|
|
else
|
|
count--;
|
|
|
|
GB.Component.GetInfo("SET_EVENT_FILTER", POINTER(&set_event_filter));
|
|
if (set_event_filter)
|
|
(*set_event_filter)(count ? X11_event_filter : NULL);
|
|
}
|
|
|
|
void X11_event_filter(XEvent *e)
|
|
{
|
|
static bool init = FALSE;
|
|
|
|
if (!init)
|
|
{
|
|
GB_CLASS startup = GB.Application.StartupClass();
|
|
GB.GetFunction(&_x11_property_notify_func, (void *)startup, "X11_PropertyNotify", "ii", "");
|
|
GB.GetFunction(&_x11_configure_notify_func, (void *)startup, "X11_ConfigureNotify", "iiiii", "");
|
|
init = TRUE;
|
|
}
|
|
|
|
if (e->type == PropertyNotify && GB_FUNCTION_IS_VALID(&_x11_property_notify_func))
|
|
{
|
|
GB.Push(2, GB_T_INTEGER, e->xany.window, GB_T_INTEGER, e->xproperty.atom);
|
|
GB.Call(&_x11_property_notify_func, 2, TRUE);
|
|
}
|
|
else if (e->type == ConfigureNotify && GB_FUNCTION_IS_VALID(&_x11_configure_notify_func))
|
|
{
|
|
GB.Push(5, GB_T_INTEGER, e->xany.window,
|
|
GB_T_INTEGER, e->xconfigure.x,
|
|
GB_T_INTEGER, e->xconfigure.y,
|
|
GB_T_INTEGER, e->xconfigure.width,
|
|
GB_T_INTEGER, e->xconfigure.height);
|
|
|
|
GB.Call(&_x11_configure_notify_func, 5, TRUE);
|
|
}
|
|
|
|
WATCHER_event_filter(e);
|
|
SYSTRAY_event_filter(e);
|
|
}
|
|
|
|
void X11_get_window_geometry(Window win, int *wx, int *wy, int *ww, int *wh)
|
|
{
|
|
Window p;
|
|
int transx, transy;
|
|
XWindowAttributes wattr;
|
|
|
|
*wx = *wy = *ww = *wh = 0;
|
|
|
|
if (!XTranslateCoordinates(_display, win, _root, 0, 0, &transx, &transy, &p))
|
|
return;
|
|
|
|
if (!XGetWindowAttributes(_display, win, &wattr))
|
|
return;
|
|
|
|
*wx = transx - wattr.border_width;
|
|
*wy = transy - wattr.border_width;
|
|
*ww = wattr.width + wattr.border_width * 2;
|
|
*wh = wattr.height + wattr.border_width * 2;
|
|
}
|