static void drawin_update_drawing(drawin_t *w, int widx) { luaA_object_push_item(globalconf.L, widx, w->drawable); drawable_set_geometry(w->drawable, -1, w->geometry); lua_pop(globalconf.L, 1); }
/** 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); }
static void drawin_update_drawing(lua_State *L, int widx) { drawin_t *w = luaA_checkudata(L, widx, &drawin_class); luaA_object_push_item(L, widx, w->drawable); drawable_set_geometry(L, -1, w->geometry); lua_pop(L, 1); }
/** Move and/or resize a drawin * \param L The Lua VM state. * \param udx The index of the drawin. * \param geometry The new geometry. */ static void drawin_moveresize(lua_State *L, int udx, area_t geometry) { drawin_t *w = luaA_checkudata(L, udx, &drawin_class); int number_of_vals = 0; uint32_t moveresize_win_vals[4], mask_vals = 0; if(w->geometry.x != geometry.x) { w->geometry.x = moveresize_win_vals[number_of_vals++] = geometry.x; mask_vals |= XCB_CONFIG_WINDOW_X; } if(w->geometry.y != geometry.y) { w->geometry.y = moveresize_win_vals[number_of_vals++] = geometry.y; mask_vals |= XCB_CONFIG_WINDOW_Y; } if(geometry.width > 0 && w->geometry.width != geometry.width) { w->geometry.width = moveresize_win_vals[number_of_vals++] = geometry.width; mask_vals |= XCB_CONFIG_WINDOW_WIDTH; } if(geometry.height > 0 && w->geometry.height != geometry.height) { w->geometry.height = moveresize_win_vals[number_of_vals++] = geometry.height; mask_vals |= XCB_CONFIG_WINDOW_HEIGHT; } if(mask_vals & (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)) drawin_update_drawing(w, udx); else { /* We still have to set x/y */ luaA_object_push_item(L, udx, w->drawable); drawable_set_geometry(w->drawable, -1, w->geometry); lua_pop(L, 1); } /* Activate BMA */ client_ignore_enterleave_events(); if(mask_vals) xcb_configure_window(globalconf.connection, w->window, mask_vals, moveresize_win_vals); /* Deactivate BMA */ client_restore_enterleave_events(); if(mask_vals & XCB_CONFIG_WINDOW_X) luaA_object_emit_signal(L, udx, "property::x", 0); if(mask_vals & XCB_CONFIG_WINDOW_Y) luaA_object_emit_signal(L, udx, "property::y", 0); if(mask_vals & XCB_CONFIG_WINDOW_WIDTH) luaA_object_emit_signal(L, udx, "property::width", 0); if(mask_vals & XCB_CONFIG_WINDOW_HEIGHT) luaA_object_emit_signal(L, udx, "property::height", 0); }
/** 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); } }
/** Push an array of key as an Lua table onto the stack. * \param L The Lua VM state. * \param oidx The index of the object to get items from. * \param keys The key array to push. * \return The number of elements pushed on stack. */ int luaA_key_array_get(lua_State *L, int oidx, key_array_t *keys) { lua_createtable(L, keys->len, 0); for(int i = 0; i < keys->len; i++) { luaA_object_push_item(L, oidx, keys->tab[i]); lua_rawseti(L, -2, i + 1); } return 1; }
static void drawin_update_drawing(drawin_t *w, int widx) { /* If this drawin isn't visible, we don't need an up-to-date cairo surface * for it. (drawin_map() will later make sure we are called again) */ if(!w->visible) return; luaA_object_push_item(globalconf.L, widx, w->drawable); drawable_set_geometry(w->drawable, -1, w->geometry); lua_pop(globalconf.L, 1); }
/** 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); }
/** 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; if(event_handle_mousegrabber(ev->root_x, ev->root_y, 1 << (ev->detail - 1 + 8))) return 0; /* 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((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->window == ev->child) { ev->event_x -= wibox->geometry.x; ev->event_y -= wibox->geometry.y; } /* Push the wibox */ luaA_object_push(globalconf.L, wibox); /* Duplicate the wibox */ lua_pushvalue(globalconf.L, -1); /* Handle the button event on it */ event_button_callback(ev, &wibox->buttons, -1, 1, NULL); /* then try to match a widget binding */ widget_t *w = widget_getbycoords(wibox->orientation, &wibox->widgets, wibox->geometry.width, wibox->geometry.height, &ev->event_x, &ev->event_y); if(w) { /* Push widget (the wibox is already on stack) */ luaA_object_push_item(globalconf.L, -1, w); /* Move widget before wibox */ lua_insert(globalconf.L, -2); event_button_callback(ev, &w->buttons, -2, 2, NULL); } else /* Remove the wibox, we did not use it */ lua_pop(globalconf.L, 1); } else if((c = client_getbywin(ev->event))) { luaA_object_push(globalconf.L, c); event_button_callback(ev, &c->buttons, -1, 1, NULL); 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_button_callback(ev, &globalconf.buttons, 0, 0, NULL); return 0; } return 0; }
/** Render a list of widgets. * \param wibox The wibox. * \todo Remove GC. */ void widget_render(wibox_t *wibox) { lua_State *L = globalconf.L; draw_context_t *ctx = &wibox->ctx; area_t rectangle = { 0, 0, 0, 0 }; color_t col; rectangle.width = ctx->width; rectangle.height = ctx->height; if (!widget_geometries(wibox)) return; if(ctx->bg.alpha != 0xffff) { int x = wibox->geometry.x + wibox->border_width, y = wibox->geometry.y + wibox->border_width; xcb_get_property_reply_t *prop_r; char *data; xcb_pixmap_t rootpix; xcb_get_property_cookie_t prop_c; xcb_screen_t *s = xutil_screen_get(globalconf.connection, ctx->phys_screen); prop_c = xcb_get_property_unchecked(globalconf.connection, false, s->root, _XROOTPMAP_ID, PIXMAP, 0, 1); if((prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL))) { if(prop_r->value_len && (data = xcb_get_property_value(prop_r)) && (rootpix = *(xcb_pixmap_t *) data)) switch(wibox->orientation) { case North: draw_rotate(ctx, rootpix, ctx->pixmap, s->width_in_pixels, s->height_in_pixels, ctx->width, ctx->height, M_PI_2, y + ctx->width, - x); break; case South: draw_rotate(ctx, rootpix, ctx->pixmap, s->width_in_pixels, s->height_in_pixels, ctx->width, ctx->height, - M_PI_2, - y, x + ctx->height); break; case East: xcb_copy_area(globalconf.connection, rootpix, wibox->pixmap, wibox->gc, x, y, 0, 0, ctx->width, ctx->height); break; } p_delete(&prop_r); } } 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(L, widgets); /* get computed geometries */ for(unsigned int i = 0; i < lua_objlen(L, -1); i++) { lua_pushnumber(L, i + 1); lua_gettable(L, -2); widgets->tab[i].geometry.x = luaA_getopt_number(L, -1, "x", wibox->geometry.x); widgets->tab[i].geometry.y = luaA_getopt_number(L, -1, "y", wibox->geometry.y); widgets->tab[i].geometry.width = luaA_getopt_number(L, -1, "width", 1); widgets->tab[i].geometry.height = luaA_getopt_number(L, -1, "height", 1); lua_pop(L, 1); } lua_pop(L, 1); /* draw background image, only if the background color is not opaque */ if(wibox->bg_image && ctx->bg.alpha != 0xffff) draw_image(ctx, 0, 0, 1.0, wibox->bg_image); /* draw background color */ xcolor_to_color(&ctx->bg, &col); draw_rectangle(ctx, rectangle, 1.0, true, &col); /* draw everything! */ for(int i = 0; i < widgets->len; i++) if(widgets->tab[i].widget->isvisible) widgets->tab[i].widget->draw(widgets->tab[i].widget, ctx, widgets->tab[i].geometry, wibox); switch(wibox->orientation) { case South: draw_rotate(ctx, ctx->pixmap, wibox->pixmap, ctx->width, ctx->height, ctx->height, ctx->width, M_PI_2, ctx->height, 0); break; case North: draw_rotate(ctx, ctx->pixmap, wibox->pixmap, ctx->width, ctx->height, ctx->height, ctx->width, - M_PI_2, 0, ctx->width); break; case East: break; } }
/** 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 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; } }