diff --git a/CMakeLists.txt b/CMakeLists.txt index 9108e2d42..dc48f86c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ set(AWE_SRCS ${SOURCE_DIR}/ewmh.c ${SOURCE_DIR}/keybinding.c ${SOURCE_DIR}/keygrabber.c + ${SOURCE_DIR}/mousegrabber.c ${SOURCE_DIR}/layout.c ${SOURCE_DIR}/luaa.c ${SOURCE_DIR}/hooks.c diff --git a/awesome.c b/awesome.c index 6291ebd53..41f04e6e9 100644 --- a/awesome.c +++ b/awesome.c @@ -327,6 +327,7 @@ main(int argc, char **argv) /* clear the globalconf structure */ p_clear(&globalconf, 1); globalconf.keygrabber = LUA_REFNIL; + globalconf.mousegrabber = LUA_REFNIL; /* save argv */ for(i = 0; i < argc; i++) diff --git a/event.c b/event.c index c29b9fdd8..77ac2e281 100644 --- a/event.c +++ b/event.c @@ -35,6 +35,7 @@ #include "titlebar.h" #include "keybinding.h" #include "keygrabber.h" +#include "mousegrabber.h" #include "luaa.h" #include "systray.h" #include "screen.h" @@ -133,6 +134,29 @@ widget_getbycoords(position_t position, widget_node_array_t *widgets, return NULL; } +/** Handle an event with mouse grabber if needed + * \param x The x coordinate. + * \param y The y coordinate. + * \param mask The mask buttons. + */ +static void +event_handle_mousegrabber(int x, int y, uint16_t mask) +{ + if(globalconf.mousegrabber != LUA_REFNIL) + { + lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.mousegrabber); + mousegrabber_handleevent(globalconf.L, x, y, mask); + if(lua_pcall(globalconf.L, 1, 1, 0)) + { + warn("error running function: %s", lua_tostring(globalconf.L, -1)); + luaA_mousegrabber_stop(globalconf.L); + } + else if(!lua_isboolean(globalconf.L, -1) || !lua_toboolean(globalconf.L, -1)) + luaA_mousegrabber_stop(globalconf.L); + lua_pop(globalconf.L, 1); /* pop returned value */ + } +} + /** The button press event handler. * \param data The type of mouse event. * \param connection The connection to the X server. @@ -153,6 +177,8 @@ event_handle_button(void *data, xcb_connection_t *connection, xcb_button_press_e * drop them */ ev->state &= 0x00ff; + event_handle_mousegrabber(ev->root_x, ev->root_y, ev->state); + if((wibox = wibox_getbywin(ev->event)) || (wibox = wibox_getbywin(ev->child))) { @@ -408,6 +434,8 @@ event_handle_motionnotify(void *data __attribute__ ((unused)), wibox_t *wibox = wibox_getbywin(ev->event); widget_t *w; + event_handle_mousegrabber(ev->root_x, ev->root_y, ev->state); + if(wibox && (w = widget_getbycoords(wibox->position, &wibox->widgets, wibox->sw.geometry.width, diff --git a/luaa.c b/luaa.c index 50a928031..8bde44694 100644 --- a/luaa.c +++ b/luaa.c @@ -56,6 +56,7 @@ extern awesome_t globalconf; extern const struct luaL_reg awesome_hooks_lib[]; extern const struct luaL_reg awesome_keygrabber_lib[]; +extern const struct luaL_reg awesome_mousegrabber_lib[]; extern const struct luaL_reg awesome_button_methods[]; extern const struct luaL_reg awesome_button_meta[]; extern const struct luaL_reg awesome_image_methods[]; @@ -769,6 +770,9 @@ luaA_init(void) /* Export keygrabber lib */ luaL_register(L, "keygrabber", awesome_keygrabber_lib); + /* Export mousegrabber lib */ + luaL_register(L, "mousegrabber", awesome_mousegrabber_lib); + /* Export otable lib */ luaA_openlib(L, "otable", otable_methods, otable_meta); diff --git a/mouse.c b/mouse.c index 5ff07b445..ae8b2f209 100644 --- a/mouse.c +++ b/mouse.c @@ -1320,6 +1320,37 @@ luaA_mouse_newindex(lua_State *L) return 0; } +/** Push a table with mouse status. + * \param L The Lua VM state. + * \param x The x coordinate. + * \param y The y coordinate. + * \param mask The button mask. + */ +int +luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask) +{ + lua_newtable(L); + lua_pushnumber(L, x); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, y); + lua_setfield(L, -2, "y"); + + lua_newtable(L); + + int i = 1; + + for(uint16_t maski = XCB_BUTTON_MASK_1; maski <= XCB_BUTTON_MASK_5; maski <<= 1) + { + if(mask & maski) + lua_pushboolean(L, true); + else + lua_pushboolean(L, false); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "buttons"); + return 1; +} + /** Get or set the mouse coords. * \param L The Lua VM state. * \return The number of elements pushed on stack. @@ -1330,8 +1361,8 @@ luaA_mouse_newindex(lua_State *L) static int luaA_mouse_coords(lua_State *L) { - uint16_t mask, maski; - int screen, x, y, mouse_x, mouse_y, i = 1; + uint16_t mask; + int screen, x, y, mouse_x, mouse_y; if(lua_gettop(L) == 1) { @@ -1353,21 +1384,7 @@ luaA_mouse_coords(lua_State *L) if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, &mask)) return 0; - lua_newtable(L); - lua_pushnumber(L, mouse_x); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, mouse_y); - lua_setfield(L, -2, "y"); - lua_newtable(L); - for(maski = XCB_BUTTON_MASK_1; i <= XCB_BUTTON_MASK_5; maski <<= 1, i++) - if(mask & maski) - { - lua_pushboolean(L, true); - lua_rawseti(L, -2, i); - } - lua_setfield(L, -2, "buttons"); - - return 1; + return luaA_mouse_pushstatus(L, mouse_x, mouse_y, mask); } const struct luaL_reg awesome_mouse_methods[] = diff --git a/mouse.h b/mouse.h index 332e355a0..63b37e01d 100644 --- a/mouse.h +++ b/mouse.h @@ -42,6 +42,7 @@ struct button_t void button_delete(button_t **); bool mouse_query_pointer(xcb_window_t, int *, int *, uint16_t *); +int luaA_mouse_pushstatus(lua_State *, int, int, uint16_t); DO_RCNT(button_t, button, button_delete) ARRAY_FUNCS(button_t *, button, button_unref) diff --git a/mousegrabber.c b/mousegrabber.c new file mode 100644 index 000000000..f392662f7 --- /dev/null +++ b/mousegrabber.c @@ -0,0 +1,139 @@ +/* + * mousegrabber.c - mouse pointer grabbing + * + * Copyright © 2008 Julien Danjou + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "mouse.h" +#include "mousegrabber.h" +#include "common/xcursor.h" + +extern awesome_t globalconf; + +/** Grab the mouse. + * \param cursor The cursor to use while grabbing. + * \return True if mouse was grabbed. + */ +static bool +mousegrabber_grab(xcb_cursor_t cursor) +{ + xcb_window_t root; + + for(int screen = 0; + screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); + screen++) + { + int x, y; + uint16_t mask; + + root = xutil_screen_get(globalconf.connection, screen)->root; + if(mouse_query_pointer(root, &x, &y, &mask)) + break; + } + + for(int i = 1000; i; i--) + { + xcb_grab_pointer_reply_t *grab_ptr_r; + xcb_grab_pointer_cookie_t grab_ptr_c = + xcb_grab_pointer_unchecked(globalconf.connection, false, root, + XCB_EVENT_MASK_BUTTON_PRESS + | XCB_EVENT_MASK_BUTTON_RELEASE + | XCB_EVENT_MASK_POINTER_MOTION, + XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, + root, cursor, XCB_CURRENT_TIME); + + if((grab_ptr_r = xcb_grab_pointer_reply(globalconf.connection, grab_ptr_c, NULL))) + { + p_delete(&grab_ptr_r); + return true; + } + usleep(1000); + } + return false; +} + +/** Handle mouse motion events. + * \param L Lua stack to push the pointer motion. + * \param ev Received mouse motion event. + */ +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 + * motion. The callback function must return a boolean value: true to + * continue grabbing, false to stop. + * The function is called with one argument: + * a table containing modifiers pointer coordinates. + * + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * + * \luastack + * + * \lparam A callback function as described above. + */ +static int +luaA_mousegrabber_run(lua_State *L) +{ + if(globalconf.mousegrabber != LUA_REFNIL) + luaL_error(L, "mousegrabber already running"); + + uint16_t cfont = xcursor_font_fromstr(luaL_checkstring(L, 2)); + + if(cfont) + { + xcb_cursor_t cursor = xcursor_new(globalconf.connection, cfont); + + luaA_registerfct(L, 1, &globalconf.mousegrabber); + + if(!mousegrabber_grab(cursor)) + { + luaA_unregister(L, &globalconf.mousegrabber); + luaL_error(L, "unable to grab mouse pointer"); + } + } + else + luaA_warn(L, "invalid cursor"); + + return 0; +} + +/** Stop grabbing the mouse pointer. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +int +luaA_mousegrabber_stop(lua_State *L) +{ + xcb_ungrab_pointer(globalconf.connection, XCB_CURRENT_TIME); + luaA_unregister(L, &globalconf.mousegrabber); + return 0; +} + +const struct luaL_reg awesome_mousegrabber_lib[] = +{ + { "run", luaA_mousegrabber_run }, + { "stop", luaA_mousegrabber_stop }, + { NULL, NULL } +}; diff --git a/mousegrabber.h b/mousegrabber.h new file mode 100644 index 000000000..87f8d7c6b --- /dev/null +++ b/mousegrabber.h @@ -0,0 +1,32 @@ +/* + * mousegrabber.h - mouse pointer grabbing header + * + * Copyright © 2008 Julien Danjou + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_MOUSEGRABBER_H +#define AWESOME_MOUSEGRABBER_H + +#include +#include + +int luaA_mousegrabber_stop(lua_State *); +void mousegrabber_handleevent(lua_State *, int, int, uint16_t); + +#endif +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/structs.h b/structs.h index 32c3c9d82..f73a52b9a 100644 --- a/structs.h +++ b/structs.h @@ -361,6 +361,8 @@ struct awesome_t struct ev_timer timer; /** The key grabber function */ luaA_ref keygrabber; + /** The mouse pointer grabber function */ + luaA_ref mousegrabber; /** Focused screen */ screen_t *screen_focus; };