/** 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); } }
/** The leave notify event handler. * \param ev The event. */ static void event_handle_leavenotify(xcb_leave_notify_event_t *ev) { lua_State *L = globalconf_get_lua_State(); client_t *c; globalconf.timestamp = ev->time; if(ev->mode != XCB_NOTIFY_MODE_NORMAL) return; /* Ignore leave with detail inferior (we were left for a window contained in * our window, so technically the pointer is still inside of this window). */ if(ev->detail != XCB_NOTIFY_DETAIL_INFERIOR && (c = client_getbyframewin(ev->event))) { luaA_object_push(L, c); luaA_object_emit_signal(L, -1, "mouse::leave", 0); lua_pop(L, 1); } lua_pushnil(L); event_drawable_under_mouse(L, -1); lua_pop(L, 1); }
/** The client message event handler. * \param ev The event. */ static void event_handle_clientmessage(xcb_client_message_event_t *ev) { /* check for startup notification messages */ if(sn_xcb_display_process_event(globalconf.sndisplay, (xcb_generic_event_t *) ev)) return; if(ev->type == WM_CHANGE_STATE) { client_t *c; if((c = client_getbywin(ev->window)) && ev->format == 32 && ev->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) { lua_State *L = globalconf_get_lua_State(); luaA_object_push(L, c); client_set_minimized(L, -1, true); lua_pop(L, 1); } } else if(ev->type == _XEMBED) xembed_process_client_message(ev); else if(ev->type == _NET_SYSTEM_TRAY_OPCODE) systray_process_client_message(ev); else ewmh_process_client_message(ev); }
/** The map request event handler. * \param ev The event. */ static void event_handle_maprequest(xcb_map_request_event_t *ev) { client_t *c; xcb_get_window_attributes_cookie_t wa_c; xcb_get_window_attributes_reply_t *wa_r; xcb_get_geometry_cookie_t geom_c; xcb_get_geometry_reply_t *geom_r; wa_c = xcb_get_window_attributes_unchecked(globalconf.connection, ev->window); if(!(wa_r = xcb_get_window_attributes_reply(globalconf.connection, wa_c, NULL))) return; if(wa_r->override_redirect) goto bailout; if(xembed_getbywin(&globalconf.embedded, ev->window)) { xcb_map_window(globalconf.connection, ev->window); xembed_window_activate(globalconf.connection, ev->window); } else if((c = client_getbywin(ev->window))) { /* Check that it may be visible, but not asked to be hidden */ if(client_on_selected_tags(c) && !c->hidden) { lua_State *L = globalconf_get_lua_State(); luaA_object_push(L, c); client_set_minimized(L, -1, false); lua_pop(L, 1); /* it will be raised, so just update ourself */ client_raise(c); } } else { geom_c = xcb_get_geometry_unchecked(globalconf.connection, ev->window); if(!(geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL))) { goto bailout; } client_manage(ev->window, geom_r, wa_r); p_delete(&geom_r); } bailout: p_delete(&wa_r); }
/** The shape notify event handler. * \param ev The event. */ static void event_handle_shape_notify(xcb_shape_notify_event_t *ev) { client_t *c = client_getbywin(ev->affected_window); if (c) { lua_State *L = globalconf_get_lua_State(); luaA_object_push(L, c); if (ev->shape_kind == XCB_SHAPE_SK_BOUNDING) luaA_object_emit_signal(L, -1, "property::shape_client_bounding", 0); if (ev->shape_kind == XCB_SHAPE_SK_CLIP) luaA_object_emit_signal(L, -1, "property::shape_client_clip", 0); lua_pop(L, 1); } }
/** 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) { lua_State *L = globalconf_get_lua_State(); client_array_take(&t->clients, i); banning_need_update(); ewmh_client_update_desktop(c); tag_client_emit_signal(t, c, "untagged"); luaA_object_unref(L, t); return; } }
static void tag_client_emit_signal(tag_t *t, client_t *c, const char *signame) { lua_State *L = globalconf_get_lua_State(); luaA_object_push(L, c); luaA_object_push(L, t); /* emit signal on client, with new tag as argument */ luaA_object_emit_signal(L, -2, signame, 1); /* re push tag */ luaA_object_push(L, t); /* move tag before client */ lua_insert(L, -2); luaA_object_emit_signal(L, -2, signame, 1); /* Remove tag */ lua_pop(L, 1); }
/** The enter notify event handler. * \param ev The event. */ static void event_handle_enternotify(xcb_enter_notify_event_t *ev) { lua_State *L = globalconf_get_lua_State(); client_t *c; drawin_t *drawin; globalconf.timestamp = ev->time; if(ev->mode != XCB_NOTIFY_MODE_NORMAL) return; if((drawin = drawin_getbywin(ev->event))) { luaA_object_push(L, drawin); luaA_object_push_item(L, -1, drawin->drawable); event_drawable_under_mouse(L, -1); lua_pop(L, 2); } if((c = client_getbyframewin(ev->event))) { luaA_object_push(L, c); /* Ignore enter with detail inferior: The pointer was previously inside * of a child window, so technically this isn't a 'real' enter. */ if (ev->detail != XCB_NOTIFY_DETAIL_INFERIOR) luaA_object_emit_signal(L, -1, "mouse::enter", 0); drawable_t *d = client_get_drawable(c, ev->event_x, ev->event_y); if (d) { luaA_object_push_item(L, -1, d); event_drawable_under_mouse(L, -1); lua_pop(L, 1); } lua_pop(L, 1); } else if (ev->event == globalconf.screen->root) { /* When there are multiple X screens with awesome running separate * instances, reset focus. */ globalconf.focus.need_update = true; } }
/** The motion notify event handler. * \param ev The event. */ static void event_handle_motionnotify(xcb_motion_notify_event_t *ev) { lua_State *L = globalconf_get_lua_State(); drawin_t *w; client_t *c; globalconf.timestamp = ev->time; if(event_handle_mousegrabber(ev->root_x, ev->root_y, ev->state)) return; if((c = client_getbyframewin(ev->event))) { luaA_object_push(L, c); lua_pushinteger(L, ev->event_x); lua_pushinteger(L, ev->event_y); luaA_object_emit_signal(L, -3, "mouse::move", 2); /* now check if a titlebar was "hit" */ int x = ev->event_x, y = ev->event_y; drawable_t *d = client_get_drawable_offset(c, &x, &y); if (d) { luaA_object_push_item(L, -1, d); event_drawable_under_mouse(L, -1); lua_pushinteger(L, x); lua_pushinteger(L, y); luaA_object_emit_signal(L, -3, "mouse::move", 2); lua_pop(L, 1); } lua_pop(L, 1); } if((w = drawin_getbywin(ev->event))) { luaA_object_push(L, w); luaA_object_push_item(L, -1, w->drawable); event_drawable_under_mouse(L, -1); lua_pushinteger(L, ev->event_x); lua_pushinteger(L, ev->event_y); luaA_object_emit_signal(L, -3, "mouse::move", 2); lua_pop(L, 2); } }
/** XRandR event handler for RRNotify subtype XRROutputChangeNotifyEvent */ static void event_handle_randr_output_change_notify(xcb_randr_notify_event_t *ev) { if(ev->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) { xcb_randr_output_t output = ev->u.oc.output; uint8_t connection = ev->u.oc.connection; char *output_name = NULL; const char *connection_str = NULL; xcb_randr_get_output_info_reply_t *info = NULL; lua_State *L = globalconf_get_lua_State(); info = xcb_randr_get_output_info_reply(globalconf.connection, xcb_randr_get_output_info_unchecked(globalconf.connection, output, XCB_CURRENT_TIME), NULL); if(!info) return; output_name = p_dup((char *)xcb_randr_get_output_info_name(info), xcb_randr_get_output_info_name_length(info)); switch(connection) { case XCB_RANDR_CONNECTION_CONNECTED: connection_str = "Connected"; break; case XCB_RANDR_CONNECTION_DISCONNECTED: connection_str = "Disconnected"; break; default: connection_str = "Unknown"; break; } lua_pushstring(L, output_name); lua_pushstring(L, connection_str); signal_object_emit(L, &global_signals, "screen::change", 2); p_delete(&output_name); p_delete(&info); /* The docs for RRSetOutputPrimary say we get this signal */ screen_update_primary(); } }
/** 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; }
static bool luaA_loadrc(const char *confpath, bool run) { lua_State *L = globalconf_get_lua_State(); if(!luaL_loadfile(L, confpath)) { if(run) { /* Set the conffile right now so it can be used inside the * configuration file. */ conffile = a_strdup(confpath); /* Move error handling function before function */ lua_pushcfunction(L, luaA_dofunction_on_error); lua_insert(L, -2); if(lua_pcall(L, 0, LUA_MULTRET, -2)) { const char *err = lua_tostring(L, -1); luaA_startup_error(err); fprintf(stderr, "%s\n", err); /* An error happened, so reset this. */ conffile = NULL; } else return true; } else { lua_pop(L, 1); return true; } } else { const char *err = lua_tostring(L, -1); luaA_startup_error(err); fprintf(stderr, "%s\n", err); } return false; }
void tag_unref_simplified(tag_t **tag) { lua_State *L = globalconf_get_lua_State(); luaA_object_unref(L, *tag); }
/** Inform lua that the systray needs to be updated. */ void luaA_systray_invalidate(void) { lua_State *L = globalconf_get_lua_State(); signal_object_emit(L, &global_signals, "systray::update", 0); }
void luaA_emit_refresh() { lua_State *L = globalconf_get_lua_State(); signal_object_emit(L, &global_signals, "refresh", 0); }
/** The configure event handler. * \param ev The event. */ static void event_handle_configurerequest(xcb_configure_request_event_t *ev) { client_t *c; if((c = client_getbywin(ev->window))) { area_t geometry = c->geometry; uint16_t bw = c->border_width; uint16_t tb_left = c->titlebar[CLIENT_TITLEBAR_LEFT].size; uint16_t tb_right = c->titlebar[CLIENT_TITLEBAR_RIGHT].size; uint16_t tb_top = c->titlebar[CLIENT_TITLEBAR_TOP].size; uint16_t tb_bottom = c->titlebar[CLIENT_TITLEBAR_BOTTOM].size; uint16_t deco_left = bw + tb_left; uint16_t deco_right = bw + tb_right; uint16_t deco_top = bw + tb_top; uint16_t deco_bottom = bw + tb_bottom; int16_t diff_w = 0, diff_h = 0, diff_border = 0; if(ev->value_mask & XCB_CONFIG_WINDOW_X) { int16_t diff = 0; geometry.x = ev->x; xwindow_translate_for_gravity(c->size_hints.win_gravity, deco_left, 0, deco_right, 0, &diff, NULL); geometry.x += diff; } if(ev->value_mask & XCB_CONFIG_WINDOW_Y) { int16_t diff = 0; geometry.y = ev->y; xwindow_translate_for_gravity(c->size_hints.win_gravity, 0, deco_top, 0, deco_bottom, NULL, &diff); geometry.y += diff; } if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) { uint16_t old_w = geometry.width; geometry.width = ev->width; /* The ConfigureRequest specifies the size of the client window, we want the frame */ geometry.width += tb_left + tb_right; diff_w = geometry.width - old_w; } if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { uint16_t old_h = geometry.height; geometry.height = ev->height; /* The ConfigureRequest specifies the size of the client window, we want the frame */ geometry.height += tb_top + tb_bottom; diff_h = geometry.height - old_h; } if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { lua_State *L = globalconf_get_lua_State(); diff_border = ev->border_width - bw; diff_h += diff_border; diff_w += diff_border; luaA_object_push(L, c); window_set_border_width(L, -1, ev->border_width); lua_pop(L, 1); } /* If the client resizes without moving itself, apply window gravity */ if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) { int16_t diff_x = 0, diff_y = 0; xwindow_translate_for_gravity(c->size_hints.win_gravity, diff_border, diff_border, diff_w, diff_h, &diff_x, &diff_y); if(!(ev->value_mask & XCB_CONFIG_WINDOW_X)) geometry.x += diff_x; if(!(ev->value_mask & XCB_CONFIG_WINDOW_Y)) geometry.y += diff_y; } if(!client_resize(c, geometry, false)) /* ICCCM 4.1.5 / 4.2.3, if nothing was changed, send an event saying so */ client_send_configure(c); } else if (xembed_getbywin(&globalconf.embedded, ev->window)) { /* Ignore this so that systray icons cannot resize themselves. * We decide their size! */ } else event_handle_configurerequest_configure_window(ev); }
/** The configure event handler. * \param ev The event. */ static void event_handle_configurerequest(xcb_configure_request_event_t *ev) { client_t *c; if((c = client_getbywin(ev->window))) { area_t geometry = c->geometry; uint16_t bw = c->border_width; uint16_t tb_left = c->titlebar[CLIENT_TITLEBAR_LEFT].size; uint16_t tb_right = c->titlebar[CLIENT_TITLEBAR_RIGHT].size; uint16_t tb_top = c->titlebar[CLIENT_TITLEBAR_TOP].size; uint16_t tb_bottom = c->titlebar[CLIENT_TITLEBAR_BOTTOM].size; uint16_t deco_left = bw + tb_left; uint16_t deco_right = bw + tb_right; uint16_t deco_top = bw + tb_top; uint16_t deco_bottom = bw + tb_bottom; int16_t diff_w = 0, diff_h = 0, diff_border = 0; lua_State *L = globalconf_get_lua_State(); if(ev->value_mask & XCB_CONFIG_WINDOW_X) { int16_t diff = 0; geometry.x = ev->x; xwindow_translate_for_gravity(c->size_hints.win_gravity, deco_left, 0, deco_right, 0, &diff, NULL); geometry.x += diff; } if(ev->value_mask & XCB_CONFIG_WINDOW_Y) { int16_t diff = 0; geometry.y = ev->y; xwindow_translate_for_gravity(c->size_hints.win_gravity, 0, deco_top, 0, deco_bottom, NULL, &diff); geometry.y += diff; } if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) { uint16_t old_w = geometry.width; geometry.width = ev->width; /* The ConfigureRequest specifies the size of the client window, we want the frame */ geometry.width += tb_left + tb_right; diff_w = geometry.width - old_w; } if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { uint16_t old_h = geometry.height; geometry.height = ev->height; /* The ConfigureRequest specifies the size of the client window, we want the frame */ geometry.height += tb_top + tb_bottom; diff_h = geometry.height - old_h; } if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { diff_border = ev->border_width - bw; diff_h += diff_border; diff_w += diff_border; luaA_object_push(L, c); window_set_border_width(L, -1, ev->border_width); lua_pop(L, 1); } /* If the client resizes without moving itself, apply window gravity */ if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) { int16_t diff_x = 0, diff_y = 0; xwindow_translate_for_gravity(c->size_hints.win_gravity, diff_border, diff_border, diff_w, diff_h, &diff_x, &diff_y); if(!(ev->value_mask & XCB_CONFIG_WINDOW_X)) geometry.x += diff_x; if(!(ev->value_mask & XCB_CONFIG_WINDOW_Y)) geometry.y += diff_y; } c->got_configure_request = true; /* Request the changes to be applied */ luaA_object_push(L, c); lua_pushstring(L, "ewmh"); /* context */ lua_newtable(L); /* props */ /* area, it needs to be directly in the `hints` table to comply with the "protocol" */ lua_pushstring(L, "x"); lua_pushinteger(L, geometry.x); lua_rawset(L, -3); lua_pushstring(L, "y"); lua_pushinteger(L, geometry.y); lua_rawset(L, -3); lua_pushstring(L, "width"); lua_pushinteger(L, geometry.width); lua_rawset(L, -3); lua_pushstring(L, "height"); lua_pushinteger(L, geometry.height); lua_rawset(L, -3); luaA_object_emit_signal(L, -3, "request::geometry", 2); lua_pop(L, 1); } else if (xembed_getbywin(&globalconf.embedded, ev->window)) { /* Ignore this so that systray icons cannot resize themselves. * We decide their size! * However, Xembed says that we act like a WM to the embedded window and * thus we have to send a synthetic configure notify informing the * window that its configure request was denied. */ xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry_unchecked(globalconf.connection, ev->window); xcb_translate_coordinates_cookie_t coords_cookie = xcb_translate_coordinates_unchecked(globalconf.connection, ev->window, globalconf.screen->root, 0, 0); xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(globalconf.connection, geom_cookie, NULL); xcb_translate_coordinates_reply_t *coords = xcb_translate_coordinates_reply(globalconf.connection, coords_cookie, NULL); if (geom && coords) { xwindow_configure(ev->window, (area_t) { .x = coords->dst_x, .y = coords->dst_y, .width = geom->width, .height = geom->height }, 0);
/** The button press event handler. * \param ev The event. */ static void event_handle_button(xcb_button_press_event_t *ev) { lua_State *L = globalconf_get_lua_State(); client_t *c; drawin_t *drawin; globalconf.timestamp = ev->time; { /* ev->state contains the state before the event. Compute the state * after the event for the mousegrabber. */ uint16_t state = ev->state, change = 1 << (ev->detail - 1 + 8); if (XCB_EVENT_RESPONSE_TYPE(ev) == XCB_BUTTON_PRESS) state |= change; else state &= ~change; if(event_handle_mousegrabber(ev->root_x, ev->root_y, state)) return; } /* 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; if((drawin = drawin_getbywin(ev->event)) || (drawin = drawin_getbywin(ev->child))) { /* If the drawin is child, then x,y are * relative to root window */ if(drawin->window == ev->child) { ev->event_x -= drawin->geometry.x + drawin->border_width; ev->event_y -= drawin->geometry.y + drawin->border_width; } /* Push the drawable */ luaA_object_push(L, drawin); luaA_object_push_item(L, -1, drawin->drawable); /* and handle the button raw button event */ event_emit_button(L, ev); lua_pop(L, 1); /* check if any button object matches */ event_button_callback(ev, &drawin->buttons, L, -1, 1, NULL); /* Either we are receiving this due to ButtonPress/Release on the root * window or because we grabbed the button on the window. In the later * case we have to call AllowEvents. * Use AsyncPointer instead of ReplayPointer so that the event is * "eaten" instead of being handled again on the root window. */ if(ev->child == XCB_NONE) xcb_allow_events(globalconf.connection, XCB_ALLOW_ASYNC_POINTER, ev->time); } else if((c = client_getbyframewin(ev->event)) || (c = client_getbywin(ev->event))) { /* For clicks inside of c->window, we get two events. Once because of a * passive grab on c->window and then again for c->frame_window. * Ignore the second event (identifiable by ev->child != XCB_NONE). */ if (ev->event != c->frame_window || ev->child == XCB_NONE) { luaA_object_push(L, c); if (c->window == ev->event) { /* Button event into the client itself (not titlebar), translate * into the frame window. */ ev->event_x += c->titlebar[CLIENT_TITLEBAR_LEFT].size; ev->event_y += c->titlebar[CLIENT_TITLEBAR_TOP].size; } /* And handle the button raw button event */ event_emit_button(L, ev); /* then check if a titlebar was "hit" */ if (c->frame_window == ev->event) { int x = ev->event_x, y = ev->event_y; drawable_t *d = client_get_drawable_offset(c, &x, &y); if (d) { /* Copy the event so that we can fake x/y */ xcb_button_press_event_t event = *ev; event.event_x = x; event.event_y = y; luaA_object_push_item(L, -1, d); event_emit_button(L, &event); lua_pop(L, 1); } } /* then check if any button objects match */ event_button_callback(ev, &c->buttons, L, -1, 1, NULL); } xcb_allow_events(globalconf.connection, XCB_ALLOW_REPLAY_POINTER, ev->time); } else if(ev->child == XCB_NONE) if(globalconf.screen->root == ev->event) { event_button_callback(ev, &globalconf.buttons, L, 0, 0, NULL); return; } }
void luaA_emit_startup() { lua_State *L = globalconf_get_lua_State(); signal_object_emit(L, &global_signals, "startup", 0); }