Re: A gestural pager assistant
- From: Christophe Lohr <christophe lohr cegetel net>
- To: gnome-shell-list gnome org
- Subject: Re: A gestural pager assistant
- Date: Tue, 03 Jan 2012 17:09:59 +0100
Le 03/01/2012 15:49, Jasper St. Pierre a écrit :
We've seen the Num Lock bug in other things. We're not sure what
causes it yet, but it's not our bug we swear :)
With the XGrabButton() function my code establishes a passive grab on
Control+Button1.
However, if NumLock is activated, when one press Control+Button1 the
actual triggered event is NumLock+Control+Button1; which cause no
pointer grab.
(If CapsLock is activated then it also fails).
The attached code proposes a workaround: at initialization stage I
retrieves currently activated modifiers with an XQueryPointer and add
wanted modifiers to it. (The new "-n" option avoids it, if wanted.)
It works.
However it is ugly since I can't see how to be warned when the user
press NumLock or CapsLock during the session...
Regards
Christophe
/* Gestural Pager Assistant
* (c) 2011 Christophe Lohr <clohr users sourceforge net>
*
* 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 3 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/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <getopt.h>
#include <strings.h>
#include <string.h>
// Mostly from wmctrl - Tomas Styblo <tripie cpan org>
static int client_msg(Display * dpy, Window win, Window root, char *msg,
unsigned long data) {
XEvent event;
long mask = SubstructureRedirectMask | SubstructureNotifyMask;
event.xclient.type = ClientMessage;
event.xclient.serial = 0;
event.xclient.send_event = True;
event.xclient.message_type = XInternAtom(dpy, msg, False);
event.xclient.window = win;
event.xclient.format = 32;
event.xclient.data.l[0] = data;
event.xclient.data.l[1] = CurrentTime;
event.xclient.data.l[2] = 0;
event.xclient.data.l[3] = 0;
event.xclient.data.l[4] = 0;
if (XSendEvent(dpy, root, False, mask, &event)) {
return EXIT_SUCCESS;
} else {
fprintf(stderr, "Cannot send %s event.\n", msg);
return EXIT_FAILURE;
}
}
#define MAX_PROPERTY_VALUE_LEN 4096
static unsigned char *get_property(Display *dpy, Window win,
Atom xa_prop_type, char *prop_name, unsigned long *size) {
Atom xa_prop_name;
Atom xa_ret_type;
int ret_format;
unsigned long ret_nitems;
unsigned long ret_bytes_after;
unsigned char *ret_prop;
unsigned long tmp_size;
xa_prop_name = XInternAtom(dpy, prop_name, False);
if (XGetWindowProperty(dpy, win, xa_prop_name, 0,
MAX_PROPERTY_VALUE_LEN / 4, False, xa_prop_type, &xa_ret_type,
&ret_format, &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
fprintf(stderr, "Cannot get %s property.\n", prop_name);
return NULL;
}
if (xa_ret_type != xa_prop_type) {
fprintf(stderr, "Invalid type of %s property.\n", prop_name);
XFree(ret_prop);
return NULL;
}
tmp_size = (ret_format / 8) * ret_nitems;
/* Correct 64 Architecture implementation of 32 bit data */
if(ret_format==32)
tmp_size *= sizeof(long)/4;
if (size)
*size = tmp_size;
return ret_prop;
}
static Window *get_client_list(Display *dpy, unsigned long *size) {
Window *client_list;
if ((client_list = (Window *) get_property(dpy, DefaultRootWindow(dpy),
XA_WINDOW, "_NET_CLIENT_LIST", size)) == NULL)
if ((client_list = (Window *) get_property(dpy, DefaultRootWindow(dpy),
XA_CARDINAL, "_WIN_CLIENT_LIST", size)) == NULL) {
fputs("Cannot get client list properties.\n", stderr);
return NULL;
}
*size /= sizeof(Window);
return client_list;
}
static unsigned long *get_cur_desktop(Display *dpy) {
unsigned long *cur_desktop = NULL;
if (!(cur_desktop = (unsigned long *)get_property(dpy,
DefaultRootWindow(dpy), XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) {
if (!(cur_desktop = (unsigned long *)get_property(dpy,
DefaultRootWindow(dpy), XA_CARDINAL, "_WIN_WORKSPACE", NULL))) {
fputs("Cannot get current desktop properties.\n", stderr);
return NULL;
}
}
return cur_desktop;
}
static unsigned long *get_desk_window(Display *dpy, Window win) {
unsigned long *desktop = NULL;
if ((desktop = (unsigned long *)get_property(dpy, win, XA_CARDINAL,
"_NET_WM_DESKTOP", NULL)) == NULL) {
desktop = (unsigned long *)get_property(dpy, win, XA_CARDINAL,
"_WIN_WORKSPACE", NULL);
}
return desktop;
}
static unsigned long *get_num_desktops(Display *dpy) {
unsigned long *num_desktops = NULL;
if (!(num_desktops = (unsigned long *)get_property(dpy,
DefaultRootWindow(dpy), XA_CARDINAL, "_NET_NUMBER_OF_DESKTOPS", NULL))) {
if (!(num_desktops = (unsigned long *)get_property(dpy,
DefaultRootWindow(dpy), XA_CARDINAL, "_WIN_WORKSPACE_COUNT", NULL))) {
fputs("Cannot get number of desktops properties.\n", stderr);
return NULL;
}
}
return num_desktops;
}
static Window *get_active_window(Display *dpy) {
return (Window *)get_property(dpy, DefaultRootWindow(dpy), XA_WINDOW,
"_NET_ACTIVE_WINDOW", NULL);
}
// End of code from wmctrl
int change_desktop(Display *dpy, int next) {
long target;
unsigned long *num_desktops;
unsigned long *cur_desktop;
int ret = EXIT_SUCCESS;
if (!(cur_desktop = get_cur_desktop(dpy)))
return EXIT_FAILURE;
if (!(num_desktops = get_num_desktops(dpy))) {
XFree(cur_desktop);
return EXIT_FAILURE;
}
target = next + *cur_desktop;
if (target < 0)
target = 0;
if (target >= *num_desktops)
target = *num_desktops - 1;
if (target != *cur_desktop) {
ret = client_msg(dpy, DefaultRootWindow(dpy), DefaultRootWindow(dpy),
"_NET_CURRENT_DESKTOP", target);
XSync(dpy, False);
}
XFree(num_desktops);
XFree(cur_desktop);
return ret;
}
int focus_window(Display * dpy, int next) {
Window *client_list, *active;
unsigned long client_list_size = 0;
int i, j, active_i = 0, target, step;
unsigned long *cur_desktop = NULL, *desktop = NULL;
int ret = EXIT_SUCCESS;
if (!(client_list = get_client_list(dpy, &client_list_size)))
return EXIT_FAILURE;
if (!client_list_size)
return EXIT_SUCCESS; // nothing to do
if ((active = get_active_window(dpy))) {
// retreives the active window within the clients list
for (i = 0; i < client_list_size; i++)
if (client_list[i] == *active) {
active_i = i;
break;
}
XFree(active);
if (i == client_list_size) {
// Cannot retreive active window within clients
XFree(client_list);
active_i = 0;
}
} else // no window has the focus yet, select the first one
active_i = 0;
if (!(cur_desktop = get_cur_desktop(dpy))) {
XFree(client_list);
return EXIT_FAILURE;
}
// select the "next" window of the current desktop
step = (next > 0) ? +1 : -1;
target = j = active_i;
for (i = 1; (i < client_list_size) && next; i++) {
j += step;
target = j % (int)client_list_size;
if (target < 0)
target += client_list_size;
desktop = get_desk_window(dpy, client_list[target]);
if (*desktop == *cur_desktop)
next-=step;
XFree(desktop);
}
XFree(cur_desktop);
if (!next) {
ret = client_msg(dpy, client_list[target], DefaultRootWindow(dpy),
"_NET_ACTIVE_WINDOW", 2);
XMapRaised(dpy, client_list[target]);
XSync(dpy, False);
}
XFree(client_list);
return ret;
}
int io_handler(Display *dpy) {
// Display closed, stop.
exit(EXIT_SUCCESS);
}
struct point {
int x, y;
};
void main_loop(Display *dpy, struct point amplitude, int revert,
unsigned int button, unsigned int modifiers) {
struct point start = { 0, 0 };
XEvent ev;
XGrabButton(dpy, button, modifiers, DefaultRootWindow(dpy), True,
ButtonMotionMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, None,
XCreateFontCursor(dpy, XC_fleur));
while (1) {
int vertical, horizontal;
XNextEvent(dpy, &ev);
switch (ev.type) {
case ButtonPress:
start.x = ev.xbutton.x_root;
start.y = ev.xbutton.y_root;
break;
case MotionNotify:
horizontal = (ev.xmotion.x_root - start.x) / amplitude.x;
vertical = (ev.xmotion.y_root - start.y) / amplitude.y;
if (horizontal) {
if (revert)
change_desktop(dpy, horizontal);
else
focus_window(dpy, horizontal);
start.x = ev.xbutton.x_root;
}
if (vertical) {
if (revert)
focus_window(dpy, vertical);
else
change_desktop(dpy, -vertical);
start.y = ev.xbutton.y_root;
}
break;
}
}
}
void main_loop() __attribute__((noreturn));
#define USAGE \
"Usage: %s [OPTIONS]\n" \
"Gestural pager assistant.\n" \
"Press <modifiers>+<button>, then move your mouse up/down to navigate\n" \
"to next/previous desktop, or left/right to navigate through windows\n" \
"of the current desktop.\n\n" \
"Options:\n" \
" --amplitude_x,-x <int> Vertical/horizontal amplitude of a gesture;\n" \
" --amplitude_y,-y <int> unit is (mm) millimeters, (cm) centimeters, \n" \
" or (px) pixels; a negative value inverses navigation direction.\n" \
" --button,-b <unsigned> Grab button number.\n" \
" 0:AnyButton 1:Button1 2:Button2 3:Button3 etc.\n" \
" --modifiers,-m <unsigned> Grab modifiers mask. Please compute yourself\n" \
" a bitwise inclusive OR of:\n" \
" 0:None 1:ShiftMask(1<<0) 2:LockMask(1<<1) 4:ControlMask(1<<2)\n" \
" 8:Mod1Mask(1<<3) 16:Mod2Mask(1<<4) 32:Mod3Mask(1<<5) \n" \
" 64:Mod4Mask(1<<6) 128:Mod5Mask(1<<7) 32768:AnyModifier(1<<15)\n" \
" --no_add_mod,-n Do not add currently activated modifiers if any\n" \
" such as num lock (Mod2Mask), caps lock (LockMask), etc.\n" \
" --display,-d <string> X11 display to connect to.\n" \
" --revert,-r Revert gestures actions.\n" \
" Up/down gestures navigates through windows, left/right through desks.\n" \
" --help,-h Print this help and exit.\n\n" \
"Default is: %s -m 1 -b 1 -x 3cm -y 3cm\n" \
"i.e.: Press Control+Button1, then have gesture amplitude of 3cm.\n"
int main(int argc, char **argv) {
char *display_name = NULL;
Display *dpy;
Screen *screen;
struct point size, size_mm;
struct point amplitude = {0, 0}, amp_mm = {30, 30};
unsigned int button = Button1;
unsigned int modifiers = ControlMask;
int no_add_mod = 0;
int revert = 0;
int c, unexpected = 0;
char *endptr;
long val;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"amplitude_x", required_argument, 0, 'x'},
{"amplitude_y", required_argument, 0, 'y'},
{"button", required_argument, 0, 'b'},
{"display", required_argument, 0, 'd'},
{"modifiers", required_argument, 0, 'm'},
{"no_add_mod", no_argument, 0, 'n'},
{"revert", no_argument, 0, 'r'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0} };
c = getopt_long(argc, argv, "x:y:b:m:d:rh", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'x':
errno = 0;
val = strtol(optarg, &endptr, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
|| (errno != 0 && val == 0)) {
perror("strtol on `amplitude_x'");
unexpected = 1;
} else {
if ( (val < INT_MIN) || (val > INT_MAX) ) {
fprintf(stderr, "Amplitude_x %ld out of bounds.\n", val);
unexpected = 1;
}
if ( endptr == optarg ) {
fprintf(stderr, "Amplitude_x `%s' is not number.\n", optarg);
unexpected = 1;
}
if ( strcasecmp(endptr, "px") == 0 )
amplitude.x = val;
else if ( strcasecmp(endptr, "mm") == 0 )
amp_mm.x = val;
else if ( strcasecmp(endptr, "cm") == 0 )
amp_mm.x = 10 * val;
else {
fprintf(stderr, "Unexpected unit `%s' for amplitude_x.\n", endptr);
unexpected = 1;
}
}
break;
case 'y':
errno = 0;
val = strtol(optarg, &endptr, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
|| (errno != 0 && val == 0)) {
perror("strtol on `amplitude_y'");
unexpected = 1;
} else {
if ( (val < INT_MIN) || (val > INT_MAX) ) {
fprintf(stderr, "Amplitude_y %ld out of bounds.\n", val);
unexpected = 1;
}
if ( endptr == optarg ) {
fprintf(stderr, "Amplitude_y `%s' is not number.\n", optarg);
unexpected = 1;
}
if ( strcasecmp(endptr, "px") == 0 )
amplitude.y = val;
else if ( strcasecmp(endptr, "mm") == 0 )
amp_mm.y = val;
else if ( strcasecmp(endptr, "cm") == 0 )
amp_mm.y = 10 * val;
else {
fprintf(stderr, "Unexpected unit `%s' for amplitude_y.\n", endptr);
unexpected = 1;
}
}
break;
case 'b':
val = atol(optarg);
if ( (val < 0) || (val > UINT_MAX) ) {
fprintf(stderr, "Invalid button number %ld.\n", val);
unexpected = 1;
} else
button = val;
break;
case 'm':
errno = 0;
val = strtol(optarg, &endptr, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
|| (errno != 0 && val == 0)) {
perror("strtol on `modifiers mask'");
unexpected = 1;
} else {
if ( (val < 0) || (val > UINT_MAX) ) {
fprintf(stderr, "Modifier mask %ld out of bounds.\n", val);
unexpected = 1;
}
if ( (endptr == optarg) || (*endptr != '\0') ) {
fprintf(stderr, "Modifier mask `%s' is not number.\n", optarg);
unexpected = 1;
}
modifiers = val;
}
break;
case 'n':
no_add_mod = 1;
break;
case 'd':
display_name = optarg;
break;
case 'r':
revert = 1;
break;
case 'h':
printf(USAGE, argv[0], argv[0]);
exit(EXIT_SUCCESS);
break;
default:
unexpected = 1;
}
}
while (optind < argc) {
fprintf(stderr, "Unexpected argument `%s'\n", argv[optind++]);
unexpected = 1;
}
if (unexpected) {
fprintf(stderr, "Try: %s --help\n", argv[0]);
exit(EXIT_FAILURE);
}
if (! (dpy = XOpenDisplay(display_name)) ) {
fprintf(stderr, "Cannot open display %s\n", XDisplayName(display_name));
exit(EXIT_FAILURE);
}
XSetIOErrorHandler(io_handler);
if (!no_add_mod) {
Window root_return, child_return;
int root_x_return, root_y_return, win_x_return, win_y_return;
unsigned int mask_return;
XQueryPointer(dpy, DefaultRootWindow(dpy),
&root_return, &child_return,
&root_x_return, &root_y_return,
&win_x_return, &win_y_return, &mask_return);
modifiers |= mask_return;
}
screen = XDefaultScreenOfDisplay(dpy);
size.x = XWidthOfScreen(screen);
size.y = XHeightOfScreen(screen);
size_mm.x = XWidthMMOfScreen(screen);
size_mm.y = XHeightMMOfScreen(screen);
if (!amplitude.x)
amplitude.x = amp_mm.x * size.x / size_mm.x;
if (!amplitude.y)
amplitude.y = amp_mm.y * size.y / size_mm.y;
main_loop(dpy, amplitude, revert, button, modifiers);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]