/** The key press event handler. * \param ev The event. */ static void event_handle_key(xcb_key_press_event_t *ev) { lua_State *L = globalconf_get_lua_State(); globalconf.timestamp = ev->time; if(globalconf.keygrabber != LUA_REFNIL) { if(keygrabber_handlekpress(L, ev)) { lua_rawgeti(L, LUA_REGISTRYINDEX, globalconf.keygrabber); if(!luaA_dofunction(L, 3, 0)) { warn("Stopping keygrabber."); luaA_keygrabber_stop(L); } } } else { /* get keysym ignoring all modifiers */ xcb_keysym_t keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0); client_t *c; if((c = client_getbywin(ev->event)) || (c = client_getbynofocuswin(ev->event))) { luaA_object_push(L, c); event_key_callback(ev, &c->keys, L, -1, 1, &keysym); } else event_key_callback(ev, &globalconf.keys, L, 0, 0, &keysym); } }
/** Remove a tag from screen. Tag must be on a screen and have no clients. * \param tag The tag to remove. */ static void tag_remove_from_screen(tag_t *tag) { int screen = tag->screen; int phys_screen = screen_virttophys(tag->screen); tag_array_t *tags = &globalconf.screens[tag->screen].tags; for(int i = 0; i < tags->len; i++) if(tags->tab[i] == tag) { tag_array_take(tags, i); break; } ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); tag->screen = SCREEN_UNDEF; tag_unref(&tag); /* resave tag prop of all clients so the number of tag will be the * same */ for(client_t *c = globalconf.clients; c; c = c->next) client_saveprops_tags(c); /* call hook */ if(globalconf.hooks.tags != LUA_REFNIL) { lua_pushnumber(globalconf.L, screen + 1); luaA_dofunction(globalconf.L, globalconf.hooks.tags, 1, 0); } }
void signal_object_emit(lua_State *L, signal_array_t *arr, const char *name, int nargs) { signal_t *sigfound = signal_array_getbyid(arr, a_strhash((const unsigned char *) name)); if(sigfound) { int nbfunc = sigfound->sigfuncs.len; luaL_checkstack(L, lua_gettop(L) + nbfunc + nargs + 1, "too much signal"); /* Push all functions and then execute, because this list can change * while executing funcs. */ foreach(func, sigfound->sigfuncs) luaA_object_push(L, (void *) *func); for(int i = 0; i < nbfunc; i++) { /* push all args */ for(int j = 0; j < nargs; j++) lua_pushvalue(L, - nargs - nbfunc + i); /* push first function */ lua_pushvalue(L, - nargs - nbfunc + i); /* remove this first function */ lua_remove(L, - nargs - nbfunc - 1 + i); luaA_dofunction(L, nargs, 0); } } /* remove args */ lua_pop(L, nargs); }
/** Emit a signal to an object. * \param L The Lua VM state. * \param oud The object index on the stack. * \param name The name of the signal. * \param nargs The number of arguments to pass to the called functions. */ void luaA_object_emit_signal(lua_State *L, int oud, const char *name, int nargs) { int oud_abs = luaA_absindex(L, oud); lua_object_t *obj = lua_touserdata(L, oud); if(!obj) luaL_error(L, "trying to emit signal on non-object"); signal_t *sigfound = signal_array_getbyid(&obj->signals, a_strhash((const unsigned char *) name)); if(sigfound) { int nbfunc = sigfound->sigfuncs.len; luaL_checkstack(L, lua_gettop(L) + nbfunc + nargs + 2, "too much signal"); /* Push all functions and then execute, because this list can change * while executing funcs. */ foreach(func, sigfound->sigfuncs) luaA_object_push_item(L, oud_abs, (void *) *func); for(int i = 0; i < nbfunc; i++) { /* push object */ lua_pushvalue(L, oud_abs); /* push all args */ for(int j = 0; j < nargs; j++) lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* push first function */ lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* remove this first function */ lua_remove(L, - nargs - nbfunc - 2 + i); luaA_dofunction(L, nargs + 1, 0); } } lua_pop(L, nargs); }
/** Remove a tag from screen. Tag must be on a screen and have no clients. * \param tag The tag to remove. */ static void tag_remove_from_screen(tag_t *tag) { int screen = tag->screen; int phys_screen = screen_virttophys(tag->screen); tag_array_t *tags = &globalconf.screens[tag->screen].tags; for(int i = 0; i < tags->len; i++) if(tags->tab[i] == tag) { tag_array_take(tags, i); break; } ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); tag->screen = SCREEN_UNDEF; /* call hook */ if(globalconf.hooks.tags != LUA_REFNIL) { lua_pushnumber(globalconf.L, screen + 1); tag_push(globalconf.L, tag); lua_pushliteral(globalconf.L, "remove"); luaA_dofunction(globalconf.L, globalconf.hooks.tags, 3, 0); } tag_unref(globalconf.L, tag); }
/** Handle mouse button events. * \param c The client on which the event happened or NULL. * \param type Event type, press or release. * \param button Button number. * \param state Modkeys state. * \param buttons Buttons array to check for. */ static void event_handle_mouse_button(client_t *c, uint8_t type, xcb_button_t button, uint16_t state, button_array_t *buttons) { for(int i = 0; i < buttons->len; i++) if(button == buttons->tab[i]->button && state == buttons->tab[i]->mod) switch(type) { case XCB_BUTTON_PRESS: if(buttons->tab[i]->press != LUA_REFNIL) { if(c) { client_push(globalconf.L, c); luaA_dofunction(globalconf.L, buttons->tab[i]->press, 1, 0); } else luaA_dofunction(globalconf.L, buttons->tab[i]->press, 0, 0); } break; case XCB_BUTTON_RELEASE: if(buttons->tab[i]->release != LUA_REFNIL) { if(c) { client_push(globalconf.L, c); luaA_dofunction(globalconf.L, buttons->tab[i]->release, 1, 0); } else luaA_dofunction(globalconf.L, buttons->tab[i]->release, 0, 0); } break; } }
/** Emit a signal. * @tparam string name A signal name. * @param[opt] ... Various arguments. * @function emit_signal */ void luaA_object_emit_signal(lua_State *L, int oud, const char *name, int nargs) { int oud_abs = luaA_absindex(L, oud); lua_class_t *lua_class = luaA_class_get(L, oud); lua_object_t *obj = luaA_toudata(L, oud, lua_class); if(!obj) { luaA_warn(L, "Trying to emit signal '%s' on non-object", name); return; } else if(lua_class->checker && !lua_class->checker(obj)) { luaA_warn(L, "Trying to emit signal '%s' on invalid object", name); return; } signal_t *sigfound = signal_array_getbyid(&obj->signals, a_strhash((const unsigned char *) name)); if(sigfound) { int nbfunc = sigfound->sigfuncs.len; luaL_checkstack(L, lua_gettop(L) + nbfunc + nargs + 2, "too much signal"); /* Push all functions and then execute, because this list can change * while executing funcs. */ foreach(func, sigfound->sigfuncs) luaA_object_push_item(L, oud_abs, *func); for(int i = 0; i < nbfunc; i++) { /* push object */ lua_pushvalue(L, oud_abs); /* push all args */ for(int j = 0; j < nargs; j++) lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* push first function */ lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* remove this first function */ lua_remove(L, - nargs - nbfunc - 2 + i); luaA_dofunction(L, nargs + 1, 0); } } else { luaA_warn(L, "Trying to emit unknown signal '%s'", name); return; } /* Then emit signal on the class */ lua_pushvalue(L, oud); lua_insert(L, - nargs - 1); luaA_class_emit_signal(L, luaA_class_get(L, - nargs - 1), name, nargs + 1); }
/** Tag a client with specified tag. * \param c the client to tag * \param t the tag to tag the client with */ void tag_client(client_t *c, tag_t *t) { /* don't tag twice */ if(is_client_tagged(c, t)) return; tag_ref(&t); client_array_append(&t->clients, c); client_saveprops_tags(c); client_need_arrange(c); /* call hook */ if(globalconf.hooks.tagged != LUA_REFNIL) { luaA_client_userdata_new(globalconf.L, c); luaA_tag_userdata_new(globalconf.L, t); luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 2, 0); } }
/** Untag a client with specified tag. * \param c the client to tag * \param t the tag to tag the client with */ void untag_client(client_t *c, tag_t *t) { for(int i = 0; i < t->clients.len; i++) if(t->clients.tab[i] == c) { client_need_arrange(c); client_array_take(&t->clients, i); client_saveprops_tags(c); /* call hook */ if(globalconf.hooks.tagged != LUA_REFNIL) { luaA_client_userdata_new(globalconf.L, c); luaA_tag_userdata_new(globalconf.L, t); luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 2, 0); } tag_unref(&t); return; } }
/** Untag a client with specified tag. * \param c the client to tag * \param t the tag to tag the client with */ void untag_client(client_t *c, tag_t *t) { for(int i = 0; i < t->clients.len; i++) if(t->clients.tab[i] == c) { client_need_arrange(c); client_array_take(&t->clients, i); ewmh_client_update_desktop(c); /* call hook */ if(globalconf.hooks.tagged != LUA_REFNIL) { client_push(globalconf.L, c); tag_push(globalconf.L, t); luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 2, 0); } tag_unref(globalconf.L, t); return; } }
/** Tag a client with the tag on top of the stack. * \param c the client to tag */ void tag_client(client_t *c) { tag_t *t = tag_ref(globalconf.L); /* don't tag twice */ if(is_client_tagged(c, t)) return; client_array_append(&t->clients, c); ewmh_client_update_desktop(c); client_need_arrange(c); /* call hook */ if(globalconf.hooks.tagged != LUA_REFNIL) { client_push(globalconf.L, c); tag_push(globalconf.L, t); luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 2, 0); } }
/** Handle an event with mouse grabber if needed * \param x The x coordinate. * \param y The y coordinate. * \param mask The mask buttons. * \return True if the event was handled. */ static bool event_handle_mousegrabber(int x, int y, uint16_t mask) { if(globalconf.mousegrabber != LUA_REFNIL) { lua_State *L = globalconf_get_lua_State(); mousegrabber_handleevent(L, x, y, mask); lua_rawgeti(L, LUA_REGISTRYINDEX, globalconf.mousegrabber); if(!luaA_dofunction(L, 1, 1)) { warn("Stopping mousegrabber."); luaA_mousegrabber_stop(L); } else { if(!lua_isboolean(L, -1) || !lua_toboolean(L, -1)) luaA_mousegrabber_stop(L); lua_pop(L, 1); /* pop returned value */ } return true; } return false; }
/** Append a tag which on top of the stack to a screen. * \param screen the screen id */ void tag_append_to_screen(screen_t *s) { int phys_screen = screen_virttophys(s->index); tag_t *tag = tag_ref(globalconf.L); tag->screen = s->index; tag_array_append(&s->tags, tag); ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); /* call hook */ if(globalconf.hooks.tags != LUA_REFNIL) { lua_pushnumber(globalconf.L, s->index + 1); tag_push(globalconf.L, tag); lua_pushliteral(globalconf.L, "add"); luaA_dofunction(globalconf.L, globalconf.hooks.tags, 3, 0); } }
/** Append a tag to a screen. * \param tag the tag to append * \param screen the screen id */ void tag_append_to_screen(tag_t *tag, screen_t *s) { int phys_screen = screen_virttophys(s->index); tag->screen = s->index; tag_array_append(&s->tags, tag_ref(&tag)); ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); /* resave tag prop of all clients so the number of tag will be the * same */ for(client_t *c = globalconf.clients; c; c = c->next) client_saveprops_tags(c); /* call hook */ if(globalconf.hooks.tags != LUA_REFNIL) { lua_pushnumber(globalconf.L, s->index + 1); luaA_dofunction(globalconf.L, globalconf.hooks.tags, 1, 0); } }
/** Retrieve a list of widget geometries using a Lua layout function. * a table which contains the geometries is then pushed onto the stack * \param wibox The wibox. * \return True is everything is ok, false otherwise. */ static bool widget_geometries(wibox_t *wibox) { /* get the layout field of the widget table */ if(wibox->widgets_table) { /* push wibox */ luaA_object_push(globalconf.L, wibox); /* push widgets table */ luaA_object_push_item(globalconf.L, -1, wibox->widgets_table); /* remove wibox */ lua_remove(globalconf.L, -2); /* get layout field from the table */ lua_getfield(globalconf.L, -1, "layout"); /* remove the widget table */ lua_remove(globalconf.L, -2); } else lua_pushnil(globalconf.L); /* if the layout field is a function */ if(lua_isfunction(globalconf.L, -1)) { /* Push 1st argument: wibox geometry */ area_t geometry = wibox->geometry; geometry.x = 0; geometry.y = 0; /* we need to exchange the width and height of the wibox window if it * it is rotated, so the layout function doesn't need to care about that */ if(wibox->orientation != East) { int i = geometry.height; geometry.height = geometry.width; geometry.width = i; } luaA_pusharea(globalconf.L, geometry); /* Push 2nd argument: widget table */ luaA_object_push(globalconf.L, wibox); luaA_object_push_item(globalconf.L, -1, wibox->widgets_table); lua_remove(globalconf.L, -2); /* Push 3rd argument: wibox screen */ lua_pushnumber(globalconf.L, screen_array_indexof(&globalconf.screens, wibox->screen)); /* Re-push the layout function */ lua_pushvalue(globalconf.L, -4); /* call the layout function with 3 arguments (wibox geometry, widget * table, screen) and wait for one result */ if(!luaA_dofunction(globalconf.L, 3, 1)) return false; /* Remove the left over layout function */ lua_remove(globalconf.L, -2); } else { /* Remove the "nil function" */ lua_pop(globalconf.L, 1); /* If no layout function has been specified, we just push a table with * geometries onto the stack. These geometries are nothing fancy, they * have x = y = 0 and their height and width set to the widgets demands * or the wibox size, depending on which is less. */ widget_node_array_t *widgets = &wibox->widgets; widget_node_array_wipe(widgets); widget_node_array_init(widgets); /* push wibox */ luaA_object_push(globalconf.L, wibox); /* push widgets table */ luaA_object_push_item(globalconf.L, -1, wibox->widgets_table); /* remove wibox */ lua_remove(globalconf.L, -2); luaA_table2widgets(globalconf.L, widgets); lua_newtable(globalconf.L); for(int i = 0; i < widgets->len; i++) { lua_pushnumber(globalconf.L, i + 1); widget_t *widget = widgets->tab[i].widget; lua_pushnumber(globalconf.L, screen_array_indexof(&globalconf.screens, wibox->screen)); area_t geometry = widget->extents(globalconf.L, widget); lua_pop(globalconf.L, 1); geometry.x = geometry.y = 0; geometry.width = MIN(wibox->geometry.width, geometry.width); geometry.height = MIN(wibox->geometry.height, geometry.height); luaA_pusharea(globalconf.L, geometry); lua_settable(globalconf.L, -3); } } return true; }
/** The button press event handler. * \param data The type of mouse event. * \param connection The connection to the X server. * \param ev The event. */ static int event_handle_button(void *data, xcb_connection_t *connection, xcb_button_press_event_t *ev) { int screen; const int nb_screen = xcb_setup_roots_length(xcb_get_setup(connection)); client_t *c; wibox_t *wibox; /* ev->state is * button status (8 bits) + modifiers status (8 bits) * we don't care for button status that we get, especially on release, so * 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))) { /* If the wibox is child, then x,y are * relative to root window */ if(wibox->sw.window == ev->child) { ev->event_x -= wibox->sw.geometry.x; ev->event_y -= wibox->sw.geometry.y; } /* check if we match a binding on the wibox */ button_array_t *b = &wibox->buttons; for(int i = 0; i < b->len; i++) if(ev->detail == b->tab[i]->button && ev->state == b->tab[i]->mod) switch(ev->response_type) { case XCB_BUTTON_PRESS: if(b->tab[i]->press != LUA_REFNIL) { wibox_push(globalconf.L, wibox); luaA_dofunction(globalconf.L, b->tab[i]->press, 1, 0); } break; case XCB_BUTTON_RELEASE: if(b->tab[i]->release != LUA_REFNIL) { wibox_push(globalconf.L, wibox); luaA_dofunction(globalconf.L, b->tab[i]->release, 1, 0); } break; } /* then try to match a widget binding */ widget_t *w = widget_getbycoords(wibox->position, &wibox->widgets, wibox->sw.geometry.width, wibox->sw.geometry.height, &ev->event_x, &ev->event_y); if(w) { b = &w->buttons; for(int i = 0; i < b->len; i++) if(ev->detail == b->tab[i]->button && ev->state == b->tab[i]->mod) switch(ev->response_type) { case XCB_BUTTON_PRESS: if(b->tab[i]->press != LUA_REFNIL) { wibox_push(globalconf.L, wibox); luaA_dofunction(globalconf.L, b->tab[i]->press, 1, 0); } break; case XCB_BUTTON_RELEASE: if(b->tab[i]->release != LUA_REFNIL) { wibox_push(globalconf.L, wibox); luaA_dofunction(globalconf.L, b->tab[i]->release, 1, 0); } break; } } /* return even if no widget match */ return 0; } else if((c = client_getbywin(ev->event))) { event_handle_mouse_button(c, ev->response_type, ev->detail, ev->state, &c->buttons); xcb_allow_events(globalconf.connection, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME); } else if(ev->child == XCB_NONE) { for(screen = 0; screen < nb_screen; screen++) if(xutil_screen_get(connection, screen)->root == ev->event) { event_handle_mouse_button(NULL, ev->response_type, ev->detail, ev->state, &globalconf.buttons); return 0; } } return 0; }