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, *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); } } else luaA_warn(L, "Trying to emit unknown signal '%s'", name); /* remove args */ lua_pop(L, nargs); }
/** Send fake events. Usually the current focused client will get it. * \param L The Lua VM state. * \return The number of element pushed on stack. * \luastack * \lparam The event type: key_press, key_release, button_press, button_release * or motion_notify. * \lparam The detail: in case of a key event, this is the keycode to send, in * case of a button event this is the number of the button. In case of a motion * event, this is a boolean value which if true make the coordinates relatives. * \lparam In case of a motion event, this is the X coordinate. * \lparam In case of a motion event, this is the Y coordinate. * If not specified, the current one is used. */ static int luaA_root_fake_input(lua_State *L) { if(!globalconf.have_xtest) { luaA_warn(L, "XTest extension is not available, cannot fake input."); return 0; } const char *stype = luaL_checkstring(L, 1); uint8_t type, detail; int x = 0, y = 0; if (A_STREQ(stype, "key_press")) { type = XCB_KEY_PRESS; if(lua_type(L, 2) == LUA_TSTRING) { detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */ } else { detail = luaL_checknumber(L, 2); /* keycode */ } } else if(A_STREQ(stype, "key_release")) { type = XCB_KEY_RELEASE; if(lua_type(L, 2) == LUA_TSTRING) { detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */ } else { detail = luaL_checknumber(L, 2); /* keycode */ } } else if(A_STREQ(stype, "button_press")) { type = XCB_BUTTON_PRESS; detail = luaL_checknumber(L, 2); /* button number */ } else if(A_STREQ(stype, "button_release")) { type = XCB_BUTTON_RELEASE; detail = luaL_checknumber(L, 2); /* button number */ } else if(A_STREQ(stype, "motion_notify")) { type = XCB_MOTION_NOTIFY; detail = luaA_checkboolean(L, 2); /* relative to the current position or not */ x = luaL_checknumber(L, 3); y = luaL_checknumber(L, 4); } else return 0; xcb_test_fake_input(globalconf.connection, type, detail, XCB_CURRENT_TIME, XCB_NONE, x, y, 0); return 0; }
/** 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 func A callback function as described above. * @param cursor The name of a X cursor to use while grabbing. * @function run */ 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.cursor_ctx, 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; }
/** Send fake events. Usually the current focused client will get it. * \param L The Lua VM state. * \return The number of element pushed on stack. * \luastack * \lparam The event type: key_press, key_release, button_press, button_release * or motion_notify. * \lparam The detail: in case of a key event, this is the keycode to send, in * case of a button event this is the number of the button. In case of a motion * event, this is a boolean value which if true make the coordinates relatives. * \lparam In case of a motion event, this is the X coordinate. * \lparam In case of a motion event, this is the Y coordinate. * \lparam In case of a motion event, this is the screen number to move on. * If not specified, the current one is used. */ static int luaA_root_fake_input(lua_State *L) { if(!globalconf.have_xtest) { luaA_warn(L, "XTest extension is not available, cannot fake input."); return 0; } size_t tlen; const char *stype = luaL_checklstring(L, 1, &tlen); uint8_t type, detail; int x = 0, y = 0; xcb_window_t root = XCB_NONE; switch(a_tokenize(stype, tlen)) { case A_TK_KEY_PRESS: type = XCB_KEY_PRESS; detail = luaL_checknumber(L, 2); /* keycode */ break; case A_TK_KEY_RELEASE: type = XCB_KEY_RELEASE; detail = luaL_checknumber(L, 2); /* keycode */ break; case A_TK_BUTTON_PRESS: type = XCB_BUTTON_PRESS; detail = luaL_checknumber(L, 2); /* button number */ break; case A_TK_BUTTON_RELEASE: type = XCB_BUTTON_RELEASE; detail = luaL_checknumber(L, 2); /* button number */ break; case A_TK_MOTION_NOTIFY: type = XCB_MOTION_NOTIFY; detail = luaA_checkboolean(L, 2); /* relative to the current position or not */ x = luaL_checknumber(L, 3); y = luaL_checknumber(L, 4); if(lua_gettop(L) == 5 && !globalconf.xinerama_is_active) { int screen = luaL_checknumber(L, 5) - 1; luaA_checkscreen(screen); root = xutil_screen_get(globalconf.connection, screen)->root; } break; default: return 0; } xcb_test_fake_input(globalconf.connection, type, detail, XCB_CURRENT_TIME, root, x, y, 0); return 0; }
static int luaA_timer_stop(lua_State *L) { atimer_t *timer = luaA_checkudata(L, 1, &timer_class); if(timer->started) { g_source_remove(timer->source_id); luaA_object_unref(L, timer); timer->started = false; } else luaA_warn(L, "timer not started"); return 0; }
static int luaA_timer_start(lua_State *L) { atimer_t *timer = luaA_checkudata(L, 1, &timer_class); if(timer->started) luaA_warn(L, "timer already started"); else { luaA_object_ref(L, 1); timer->started = true; timer->source_id = g_timeout_add(timer->timeout * 1000, timer_emit_signal, timer); } return 0; }
/** Set the window type. * \param L The Lua VM state. * \param window The window object. * \return The number of elements pushed on stack. */ int luaA_window_set_type(lua_State *L, window_t *w) { window_type_t type; const char *buf = luaL_checkstring(L, -1); if (A_STREQ(buf, "desktop")) type = WINDOW_TYPE_DESKTOP; else if(A_STREQ(buf, "dock")) type = WINDOW_TYPE_DOCK; else if(A_STREQ(buf, "splash")) type = WINDOW_TYPE_SPLASH; else if(A_STREQ(buf, "dialog")) type = WINDOW_TYPE_DIALOG; else if(A_STREQ(buf, "menu")) type = WINDOW_TYPE_MENU; else if(A_STREQ(buf, "toolbar")) type = WINDOW_TYPE_TOOLBAR; else if(A_STREQ(buf, "utility")) type = WINDOW_TYPE_UTILITY; else if(A_STREQ(buf, "dropdown_menu")) type = WINDOW_TYPE_DROPDOWN_MENU; else if(A_STREQ(buf, "popup_menu")) type = WINDOW_TYPE_POPUP_MENU; else if(A_STREQ(buf, "tooltip")) type = WINDOW_TYPE_TOOLTIP; else if(A_STREQ(buf, "notification")) type = WINDOW_TYPE_NOTIFICATION; else if(A_STREQ(buf, "combo")) type = WINDOW_TYPE_COMBO; else if(A_STREQ(buf, "dnd")) type = WINDOW_TYPE_DND; else if(A_STREQ(buf, "normal")) type = WINDOW_TYPE_NORMAL; else { luaA_warn(L, "Unknown window type '%s'", buf); return 0; } if(w->type != type) { w->type = type; if(w->window != XCB_WINDOW_NONE) ewmh_update_window_type(w->window, window_translate_type(w->type)); luaA_object_emit_signal(L, -3, "property::type", 0); } return 0; }
/** Set the root cursor. * \param L The Lua VM state. * \return The number of element pushed on stack. * \luastack * \lparam A X cursor name. */ static int luaA_root_cursor(lua_State *L) { const char *cursor_name = luaL_checkstring(L, 1); uint16_t cursor_font = xcursor_font_fromstr(cursor_name); if(cursor_font) { uint32_t change_win_vals[] = { xcursor_new(globalconf.connection, cursor_font) }; xcb_change_window_attributes(globalconf.connection, globalconf.screen->root, XCB_CW_CURSOR, change_win_vals); } else luaA_warn(L, "invalid cursor %s", cursor_name); return 0; }
/** Set the root cursor. * \param L The Lua VM state. * \return The number of element pushed on stack. * \luastack * \lparam A X cursor name. */ static int luaA_root_cursor(lua_State *L) { const char *cursor_name = luaL_checkstring(L, 1); uint16_t cursor_font = xcursor_font_fromstr(cursor_name); if(cursor_font) { uint32_t change_win_vals[] = { xcursor_new(globalconf.connection, cursor_font) }; for(int screen_nbr = 0; screen_nbr < xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); screen_nbr++) xcb_change_window_attributes(globalconf.connection, xutil_screen_get(globalconf.connection, screen_nbr)->root, XCB_CW_CURSOR, change_win_vals); } else luaA_warn(L, "invalid cursor %s", cursor_name); return 0; }
/** Tag newindex. * \param L The Lua VM state. * \return The number of elements pushed on stack. */ static int luaA_tag_newindex(lua_State *L) { size_t len; tag_t **tag = luaA_checkudata(L, 1, "tag"); const char *buf, *attr = luaL_checklstring(L, 2, &len); double d; int i, screen; layout_t *l; switch(a_tokenize(attr, len)) { case A_TK_NAME: buf = luaL_checklstring(L, 3, &len); p_delete(&(*tag)->name); a_iso2utf8(&(*tag)->name, buf, len); break; case A_TK_SCREEN: if(!lua_isnil(L, 3)) { screen = luaL_checknumber(L, 3) - 1; luaA_checkscreen(screen); } else screen = SCREEN_UNDEF; if((*tag)->screen != SCREEN_UNDEF) tag_remove_from_screen(*tag); if(screen != SCREEN_UNDEF) tag_append_to_screen(*tag, &globalconf.screens[screen]); break; case A_TK_LAYOUT: buf = luaL_checkstring(L, 3); l = name_func_lookup(buf, LayoutList); if(l) (*tag)->layout = l; else { luaA_warn(L, "unknown layout: %s", buf); return 0; } break; case A_TK_SELECTED: if((*tag)->screen != SCREEN_UNDEF) tag_view(*tag, luaA_checkboolean(L, 3)); return 0; case A_TK_MWFACT: d = luaL_checknumber(L, 3); if(d > 0 && d < 1) (*tag)->mwfact = d; else { luaA_warn(L, "bad value, must be between 0 and 1"); return 0; } break; case A_TK_NMASTER: i = luaL_checknumber(L, 3); if(i >= 0) (*tag)->nmaster = i; else { luaA_warn(L, "bad value, must be greater than 0"); return 0; } break; case A_TK_NCOL: i = luaL_checknumber(L, 3); if(i >= 1) (*tag)->ncol = i; else { luaA_warn(L, "bad value, must be greater than 1"); return 0; } break; default: return 0; } if((*tag)->screen != SCREEN_UNDEF && (*tag)->selected) globalconf.screens[(*tag)->screen].need_arrange = true; return 0; }
static void luaA_keystore(lua_State *L, int ud, const char *str, ssize_t len) { if(len <= 0 || !str) return; keyb_t *key = luaA_checkudata(L, ud, &key_class); if(len == 1) { key->keycode = 0; key->keysym = str[0]; } else if(str[0] == '#') { key->keycode = atoi(str + 1); key->keysym = 0; } else { key->keycode = 0; if((key->keysym = XStringToKeysym(str)) == NoSymbol ) { glong length; gunichar unicode; if(!g_utf8_validate(str, -1, NULL)) { luaA_warn(L, "failed to convert \"%s\" into keysym (invalid UTF-8 string)", str); return; } length = g_utf8_strlen(str, -1); /* This function counts combining characters. */ if(length <= 0) { luaA_warn(L, "failed to convert \"%s\" into keysym (empty UTF-8 string)", str); return; } else if(length > 1) { gchar *composed = g_utf8_normalize(str, -1, G_NORMALIZE_DEFAULT_COMPOSE); if(g_utf8_strlen(composed, -1) != 1) { p_delete(&composed); luaA_warn(L, "failed to convert \"%s\" into keysym (failed to compose a single character)", str); return; } unicode = g_utf8_get_char(composed); p_delete(&composed); } else unicode = g_utf8_get_char(str); if(unicode == (gunichar)-1 || unicode == (gunichar)-2) { luaA_warn(L, "failed to convert \"%s\" into keysym (neither keysym nor single unicode)", str); return; } /* Unicode-to-Keysym Conversion * * http://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#keysym_encoding */ if(unicode <= 0x0ff) key->keysym = unicode; else if(unicode >= 0x100 && unicode <= 0x10ffff) key->keysym = unicode | (1 << 24); else { luaA_warn(L, "failed to convert \"%s\" into keysym (unicode out of range): \"%u\"", str, unicode); return; } } } luaA_object_emit_signal(L, ud, "property::key", 0); }