Support more than 5 mouse buttons

X11 only let's you query the state of mouse button 1 to 5. However, it can
generate ButtonPress and ButtonRelease events for up to 256 mouse buttons.

Instead of asking the server which buttons are pressed, we will now remember
the button state from those ButtonPress and ButtonRelease events. Currently
this let's us keep track of up to 32 mouse buttons.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2011-08-20 15:39:49 +02:00
parent d9e4c1e866
commit bd8158495e
6 changed files with 52 additions and 35 deletions

35
event.c
View File

@ -113,7 +113,7 @@ event_handle_mousegrabber(int x, int y, uint16_t mask)
if(globalconf.mousegrabber != LUA_REFNIL) if(globalconf.mousegrabber != LUA_REFNIL)
{ {
lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.mousegrabber); lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.mousegrabber);
mousegrabber_handleevent(globalconf.L, x, y, mask); luaA_mouse_pushstatus(globalconf.L, x, y);
if(lua_pcall(globalconf.L, 1, 1, 0)) if(lua_pcall(globalconf.L, 1, 1, 0))
{ {
warn("error running function: %s", lua_tostring(globalconf.L, -1)); warn("error running function: %s", lua_tostring(globalconf.L, -1));
@ -156,6 +156,37 @@ event_emit_button(xcb_button_press_event_t *ev)
luaA_object_emit_signal(globalconf.L, -5, name, 4); luaA_object_emit_signal(globalconf.L, -5, name, 4);
} }
/** Update the global button state
* \param response_type XCB_BUTTON_PRESS or XCB_BUTTON_RELEASE
* \param button Button being changed
*/
static void
event_update_button_state(uint8_t response_type, uint8_t button)
{
/* There is no button 0! */
const unsigned int max_button = sizeof(globalconf.buttons_pressed) * 8;
uint32_t mask = 1 << (button - 1);
if (button > max_button) {
warn("Button %d pressed, but we only support %d buttons", button, max_button);
return;
}
switch(response_type)
{
case XCB_BUTTON_PRESS:
/* Set the (button-1)-st bit */
globalconf.buttons_pressed |= mask;
break;
case XCB_BUTTON_RELEASE:
/* Clear the (button-1)-st bit */
globalconf.buttons_pressed &= ~mask;
break;
default:
fatal("Invalid event type");
}
}
/** The button press event handler. /** The button press event handler.
* \param ev The event. * \param ev The event.
*/ */
@ -167,6 +198,8 @@ event_handle_button(xcb_button_press_event_t *ev)
globalconf.timestamp = ev->time; globalconf.timestamp = ev->time;
event_update_button_state(ev->response_type, ev->detail);
if(event_handle_mousegrabber(ev->root_x, ev->root_y, 1 << (ev->detail - 1 + 8))) if(event_handle_mousegrabber(ev->root_x, ev->root_y, 1 << (ev->detail - 1 + 8)))
return; return;

View File

@ -64,6 +64,8 @@ typedef struct
button_array_t buttons; button_array_t buttons;
/** Modifiers masks */ /** Modifiers masks */
uint16_t numlockmask, shiftlockmask, capslockmask, modeswitchmask; uint16_t numlockmask, shiftlockmask, capslockmask, modeswitchmask;
/** Bitmask for currently pressed buttons */
uint32_t buttons_pressed;
/** Check for XTest extension */ /** Check for XTest extension */
bool have_xtest; bool have_xtest;
/** Clients list */ /** Clients list */

34
mouse.c
View File

@ -32,11 +32,10 @@
* \param x will be set to the Pointer-x-coordinate relative to window * \param x will be set to the Pointer-x-coordinate relative to window
* \param y will be set to the Pointer-y-coordinate relative to window * \param y will be set to the Pointer-y-coordinate relative to window
* \param child Will be set to the window under the pointer. * \param child Will be set to the window under the pointer.
* \param mask will be set to the current buttons state
* \return true on success, false if an error occurred * \return true on success, false if an error occurred
**/ **/
bool static bool
mouse_query_pointer(xcb_window_t window, int16_t *x, int16_t *y, xcb_window_t *child, uint16_t *mask) mouse_query_pointer(xcb_window_t window, int16_t *x, int16_t *y, xcb_window_t *child)
{ {
xcb_query_pointer_cookie_t query_ptr_c; xcb_query_pointer_cookie_t query_ptr_c;
xcb_query_pointer_reply_t *query_ptr_r; xcb_query_pointer_reply_t *query_ptr_r;
@ -49,8 +48,6 @@ mouse_query_pointer(xcb_window_t window, int16_t *x, int16_t *y, xcb_window_t *c
*x = query_ptr_r->win_x; *x = query_ptr_r->win_x;
*y = query_ptr_r->win_y; *y = query_ptr_r->win_y;
if(mask)
*mask = query_ptr_r->mask;
if(child) if(child)
*child = query_ptr_r->child; *child = query_ptr_r->child;
@ -67,11 +64,11 @@ mouse_query_pointer(xcb_window_t window, int16_t *x, int16_t *y, xcb_window_t *c
* \return True on success, false if an error occurred. * \return True on success, false if an error occurred.
*/ */
static bool static bool
mouse_query_pointer_root(int16_t *x, int16_t *y, xcb_window_t *child, uint16_t *mask) mouse_query_pointer_root(int16_t *x, int16_t *y, xcb_window_t *child)
{ {
xcb_window_t root = globalconf.screen->root; xcb_window_t root = globalconf.screen->root;
if(mouse_query_pointer(root, x, y, child, mask)) if(mouse_query_pointer(root, x, y, child))
{ {
return true; return true;
} }
@ -106,7 +103,7 @@ luaA_mouse_index(lua_State *L)
if(a_strcmp(attr, "screen") == 0) if(a_strcmp(attr, "screen") == 0)
{ {
if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL, NULL)) if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL))
return 0; return 0;
screen = screen_getbycoord(mouse_x, mouse_y); screen = screen_getbycoord(mouse_x, mouse_y);
@ -150,7 +147,7 @@ luaA_mouse_newindex(lua_State *L)
* \param mask The button mask. * \param mask The button mask.
*/ */
int int
luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask) luaA_mouse_pushstatus(lua_State *L, int x, int y)
{ {
lua_createtable(L, 0, 2); lua_createtable(L, 0, 2);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -160,15 +157,15 @@ luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask)
lua_createtable(L, 5, 0); lua_createtable(L, 5, 0);
int i = 1; const int max_button = sizeof(globalconf.buttons_pressed) * 8;
int mask = 1;
for(uint16_t maski = XCB_BUTTON_MASK_1; maski <= XCB_BUTTON_MASK_5; maski <<= 1) for (int i = 1; i <= max_button; i++, mask <<= 1)
{ {
if(mask & maski) if(globalconf.buttons_pressed & mask)
lua_pushboolean(L, true); lua_pushboolean(L, true);
else else
lua_pushboolean(L, false); lua_pushboolean(L, false);
lua_rawseti(L, -2, i++); lua_rawseti(L, -2, i);
} }
lua_setfield(L, -2, "buttons"); lua_setfield(L, -2, "buttons");
return 1; return 1;
@ -181,7 +178,6 @@ luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask)
static int static int
luaA_mouse_coords(lua_State *L) luaA_mouse_coords(lua_State *L)
{ {
uint16_t mask;
int x, y; int x, y;
int16_t mouse_x, mouse_y; int16_t mouse_x, mouse_y;
@ -190,7 +186,7 @@ luaA_mouse_coords(lua_State *L)
luaA_checktable(L, 1); luaA_checktable(L, 1);
bool ignore_enter_notify = (lua_gettop(L) == 2 && luaA_checkboolean(L, 2)); bool ignore_enter_notify = (lua_gettop(L) == 2 && luaA_checkboolean(L, 2));
if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL, &mask)) if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL))
return 0; return 0;
x = luaA_getopt_number(L, 1, "x", mouse_x); x = luaA_getopt_number(L, 1, "x", mouse_x);
@ -207,10 +203,10 @@ luaA_mouse_coords(lua_State *L)
lua_pop(L, 1); lua_pop(L, 1);
} }
if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL, &mask)) if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL))
return 0; return 0;
return luaA_mouse_pushstatus(L, mouse_x, mouse_y, mask); return luaA_mouse_pushstatus(L, mouse_x, mouse_y);
} }
/** Get the client which is under the pointer. /** Get the client which is under the pointer.
@ -225,7 +221,7 @@ luaA_mouse_object_under_pointer(lua_State *L)
int16_t mouse_x, mouse_y; int16_t mouse_x, mouse_y;
xcb_window_t child; xcb_window_t child;
if(!mouse_query_pointer_root(&mouse_x, &mouse_y, &child, NULL)) if(!mouse_query_pointer_root(&mouse_x, &mouse_y, &child))
return 0; return 0;
drawin_t *drawin; drawin_t *drawin;

View File

@ -26,8 +26,7 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <lua.h> #include <lua.h>
bool mouse_query_pointer(xcb_window_t, int16_t *, int16_t *, xcb_window_t *, uint16_t *); int luaA_mouse_pushstatus(lua_State *, int, int);
int luaA_mouse_pushstatus(lua_State *, int, int, uint16_t);
#endif #endif
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -59,18 +59,6 @@ mousegrabber_grab(xcb_cursor_t cursor)
return false; return false;
} }
/** Handle mouse motion events.
* \param L Lua stack to push the pointer motion.
* \param x The received mouse event x component.
* \param y The received mouse event y component.
* \param mask The received mouse event bit mask.
*/
void
mousegrabber_handleevent(lua_State *L, int x, int y, uint16_t mask)
{
luaA_mouse_pushstatus(L, x, y, mask);
}
/** Grab the mouse pointer and list motions, calling callback function at each /** Grab the mouse pointer and list motions, calling callback function at each
* motion. The callback function must return a boolean value: true to * motion. The callback function must return a boolean value: true to
* continue grabbing, false to stop. * continue grabbing, false to stop.

View File

@ -26,7 +26,6 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
int luaA_mousegrabber_stop(lua_State *); int luaA_mousegrabber_stop(lua_State *);
void mousegrabber_handleevent(lua_State *, int, int, uint16_t);
#endif #endif
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80