From 862fe193eefa5908ad84036d21f8d09f08559204 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Sun, 19 Oct 2008 19:14:55 +0200 Subject: [PATCH] Rewrite {tag,task}list in Lua. This removes the C implementation of taglist and tasklist widgets and use a Lua one. This works by letting .widgets property of wiboxes to be a table with table, and setting a special metatable on them which notify awesome on newindex events, updating wiboxes. Signed-off-by: Julien Danjou --- CMakeLists.txt | 2 - awesomerc.lua.in | 84 ++++---- client.c | 9 +- common/tokenize.gperf | 2 + ewmh.c | 1 - lib/awful/titlebar.lua.in | 13 +- lib/awful/widget.lua.in | 131 +++++++++++- lib/invaders.lua.in | 16 +- lib/naughty.lua.in | 2 +- luaa.c | 260 ++++++++++++++++++++++- luaa.h | 16 +- property.c | 6 - screen.c | 3 - structs.h | 7 +- tag.c | 22 +- wibox.c | 163 +++++++-------- wibox.h | 11 +- widget.c | 67 ++++-- widget.h | 5 +- widgets/taglist.c | 306 --------------------------- widgets/tasklist.c | 430 -------------------------------------- 21 files changed, 599 insertions(+), 957 deletions(-) delete mode 100644 widgets/taglist.c delete mode 100644 widgets/tasklist.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e81aee8e..a51545a9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,8 +77,6 @@ set(AWE_SRCS ${SOURCE_DIR}/layouts/tile.c ${SOURCE_DIR}/widgets/graph.c ${SOURCE_DIR}/widgets/progressbar.c - ${SOURCE_DIR}/widgets/taglist.c - ${SOURCE_DIR}/widgets/tasklist.c ${SOURCE_DIR}/widgets/textbox.c ${SOURCE_DIR}/widgets/systray.c ${SOURCE_DIR}/widgets/imagebox.c) diff --git a/awesomerc.lua.in b/awesomerc.lua.in index c8c54dc23..78cbb6011 100644 --- a/awesomerc.lua.in +++ b/awesomerc.lua.in @@ -93,27 +93,6 @@ end -- }}} -- {{{ Wibox --- Create a taglist widget -mytaglist = widget({ type = "taglist", name = "mytaglist" }) -mytaglist:buttons({ - button({ }, 1, function (object, tag) awful.tag.viewonly(tag) end), - button({ modkey }, 1, function (object, tag) awful.client.movetotag(tag) end), - button({ }, 3, function (object, tag) tag.selected = not tag.selected end), - button({ modkey }, 3, function (object, tag) awful.client.toggletag(tag) end), - button({ }, 4, awful.tag.viewnext), - button({ }, 5, awful.tag.viewprev) -}) -mytaglist.label = awful.widget.taglist.label.all - --- Create a tasklist widget -mytasklist = widget({ type = "tasklist", name = "mytasklist" }) -mytasklist:buttons({ - button({ }, 1, function (object, c) client.focus = c; c:raise() end), - button({ }, 4, function () awful.client.focus.byidx(1) end), - button({ }, 5, function () awful.client.focus.byidx(-1) end) -}) -mytasklist.label = awful.widget.tasklist.label.currenttags - -- Create a textbox widget mytextbox = widget({ type = "textbox", name = "mytextbox", align = "right" }) -- Set the default text in textbox @@ -127,37 +106,52 @@ mylauncher = awful.widget.launcher({ name = "mylauncher", -- Create a systray mysystray = widget({ type = "systray", name = "mysystray", align = "right" }) --- Create an iconbox widget which will contains an icon indicating which layout we're using. --- We need one layoutbox per screen. -mylayoutbox = {} -for s = 1, screen.count() do - mylayoutbox[s] = widget({ type = "imagebox", name = "mylayoutbox", align = "right" }) - mylayoutbox[s]:buttons({ - button({ }, 1, function () awful.layout.inc(layouts, 1) end), - button({ }, 3, function () awful.layout.inc(layouts, -1) end), - button({ }, 4, function () awful.layout.inc(layouts, 1) end), - button({ }, 5, function () awful.layout.inc(layouts, -1) end) - }) - mylayoutbox[s].image = image("@AWESOME_ICON_PATH@/layouts/tilew.png") -end -- Create a wibox for each screen and add it mywibox = {} mypromptbox = {} +mylayoutbox = {} +mytaglist = {} +mytaglist.buttons = { button({ }, 1, awful.tag.viewonly), + button({ modkey }, 1, awful.client.movetotag), + button({ }, 3, function (tag) tag.selected = not tag.selected end), + button({ modkey }, 3, awful.client.toggletag), + button({ }, 4, awful.tag.viewnext), + button({ }, 5, awful.tag.viewprev) } +mytasklist = {} +mytasklist.buttons = { button({ }, 1, function (c) client.focus = c; c:raise() end), + button({ }, 4, function () awful.client.focus.byidx(1) end), + button({ }, 5, function () awful.client.focus.byidx(-1) end) } + for s = 1, screen.count() do - mywibox[s] = wibox({ position = "top", name = "mywibox" .. s, - fg = beautiful.fg_normal, bg = beautiful.bg_normal }) + -- Create a promptbox for each screen mypromptbox[s] = widget({ type = "textbox", name = "mypromptbox" .. s, align = "left" }) + -- Create an imagebox widget which will contains an icon indicating which layout we're using. + -- We need one layoutbox per screen. + mylayoutbox[s] = widget({ type = "imagebox", name = "mylayoutbox", align = "right" }) + mylayoutbox[s]:buttons({ button({ }, 1, function () awful.layout.inc(layouts, 1) end), + button({ }, 3, function () awful.layout.inc(layouts, -1) end), + button({ }, 4, function () awful.layout.inc(layouts, 1) end), + button({ }, 5, function () awful.layout.inc(layouts, -1) end) }) + -- Create a taglist widget + mytaglist[s] = awful.widget.taglist.new(s, awful.widget.taglist.label.all, mytaglist.buttons) + + -- Create a tasklist widget + mytasklist[s] = awful.widget.tasklist.new(function(c) + return awful.widget.tasklist.label.currenttags(c, s) + end, mytasklist.buttons) + + -- Create the wibox + mywibox[s] = wibox({ position = "top", name = "mywibox" .. s, + fg = beautiful.fg_normal, bg = beautiful.bg_normal }) -- Add widgets to the wibox - order matters - mywibox[s]:widgets({ - mytaglist, - mytasklist, - mylauncher, - mypromptbox[s], - mytextbox, - mylayoutbox[s], - s == 1 and mysystray or nil - }) + mywibox[s].widgets = { mytaglist[s], + mylauncher, + mytasklist[s], + mypromptbox[s], + mytextbox, + mylayoutbox[s], + s == 1 and mysystray or nil } mywibox[s].screen = s end -- }}} diff --git a/client.c b/client.c index dd8a19882..1177620a2 100644 --- a/client.c +++ b/client.c @@ -185,7 +185,6 @@ client_unfocus(client_t *c) luaA_client_userdata_new(globalconf.L, c); luaA_dofunction(globalconf.L, globalconf.hooks.unfocus, 1, 0); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); ewmh_update_net_active_window(c->phys_screen); } @@ -243,7 +242,6 @@ client_focus(client_t *c) luaA_dofunction(globalconf.L, globalconf.hooks.focus, 1, 0); ewmh_update_net_active_window(c->phys_screen); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); } /** Stack a window below. @@ -468,7 +466,6 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, int phys_screen, ewmh_client_strut_update(c, NULL); ewmh_update_net_client_list(c->phys_screen); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); /* Call hook to notify list change */ luaA_dofunction(globalconf.L, globalconf.hooks.clients, 0, 0); @@ -624,7 +621,6 @@ client_setfloating(client_t *c, bool floating) if(!c->isfullscreen) client_resize(c, c->f_geometry, false); client_need_arrange(c); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); client_stack(); xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, @@ -1039,8 +1035,6 @@ luaA_client_swap(lua_State *L) client_list_swap(&globalconf.clients, *swap, *c); client_need_arrange(*c); client_need_arrange(*swap); - widget_invalidate_cache((*c)->screen, WIDGET_CACHE_CLIENTS); - widget_invalidate_cache((*swap)->screen, WIDGET_CACHE_CLIENTS); /* Call hook to notify list change */ luaA_dofunction(L, globalconf.hooks.clients, 0, 0); @@ -1270,7 +1264,8 @@ luaA_client_newindex(lua_State *L) image_unref(&(*c)->icon); image_ref(image); (*c)->icon = *image; - widget_invalidate_cache((*c)->screen, WIDGET_CACHE_CLIENTS); + /* execute hook */ + hooks_property(*c, "icon"); break; case A_TK_OPACITY: if(lua_isnil(L, 3)) diff --git a/common/tokenize.gperf b/common/tokenize.gperf index ee3c4bb59..9d003d359 100644 --- a/common/tokenize.gperf +++ b/common/tokenize.gperf @@ -37,6 +37,7 @@ invert label layout left +len line Lock machine @@ -87,6 +88,7 @@ type urgent visible vertical +widgets width workarea yes diff --git a/ewmh.c b/ewmh.c index 07eeb7006..3974ea78d 100644 --- a/ewmh.c +++ b/ewmh.c @@ -333,7 +333,6 @@ ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set) luaA_client_userdata_new(globalconf.L, c); lua_pushliteral(globalconf.L, "urgent"); luaA_dofunction(globalconf.L, globalconf.hooks.property, 2, 0); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); } } diff --git a/lib/awful/titlebar.lua.in b/lib/awful/titlebar.lua.in index 98e228693..69031112d 100644 --- a/lib/awful/titlebar.lua.in +++ b/lib/awful/titlebar.lua.in @@ -80,16 +80,9 @@ function add(c, args) bts[#bts + 1] = b close:buttons(bts) - tb:widgets({ - appicon, - title, - closef, close - }) + tb.widgets = { appicon, title, closef, close } else - tb:widgets({ - appicon, - title - }) + tbx.widgets = { appicon, title } end c.titlebar = tb @@ -103,7 +96,7 @@ end -- @param prop The property name which has changed. function update(c, prop) if c.titlebar and data[c] then - local widgets = c.titlebar:widgets() + local widgets = c.titlebar.widgets local title, close, closef for k, v in ipairs(widgets) do if v.name == "title" then title = v diff --git a/lib/awful/widget.lua.in b/lib/awful/widget.lua.in index 287acc5e8..2029026c5 100644 --- a/lib/awful/widget.lua.in +++ b/lib/awful/widget.lua.in @@ -7,6 +7,7 @@ -- Grab environment we need local ipairs = ipairs local pairs = pairs +local table = table local capi = { screen = screen, @@ -16,6 +17,7 @@ local capi = mouse = mouse } local util = require("awful.util") +local hooks = require("awful.hooks") local beautiful = require("awful.beautiful") --- Widget module for awful @@ -27,6 +29,60 @@ taglist.label = {} tasklist = {} tasklist.label = {} +--- Create a new taglist widget. +-- @param screen The screen to draw tag list. +-- @param label Label function to use. +-- @param buttons A table with buttons binding to set. +function taglist.new(scr, label, buttons) + local w = {} + local function taglist_update (screen) + -- Return right now if we do not care about this screen + if scr ~= screen then return end + local tags = capi.screen[screen]:tags() + -- Hack: if it has been registered as a widget in a wibox, + -- it's w.len since __len meta does not work on table until Lua 5.2. + -- Otherwise it's standard #w. + local len = w.len or #w + -- Add more widgets + if len < #tags then + for i = len + 1, #tags do + w[i] = capi.widget({ type = "textbox", name = "taglist" .. i }) + end + -- Remove widgets + elseif len > #tags then + for i = #tags, len do + w[i] = nil + end + end + -- Update widgets text + for k, tag in ipairs(tags) do + w[k].text = label(tag) + if buttons then + -- Replace press function by a new one calling with tags as + -- argument. + -- This is done here because order of tags can change + local mbuttons = {} + for kb, b in ipairs(buttons) do + -- Copy object + mbuttons[kb] = capi.button(b) + mbuttons[kb].press = function () b.press(tag) end + end + w[k]:buttons(mbuttons) + end + end + end + hooks.arrange.register(taglist_update) + hooks.tags.register(taglist_update) + hooks.tagged.register(function (c, tag) taglist_update(c.screen) end) + hooks.property.register(function (c, prop) + if c.screen == scr and prop == "urgent" then + taglist_update(c.screen) + end + end) + taglist_update(scr) + return w +end + --- Return labels for a taglist widget with all tag from screen. -- It returns the tag name and set a special -- foreground and background color for selected tags. @@ -135,6 +191,76 @@ function taglist.label.noempty(t, args) end end +--- Create a new tasklist widget. +-- @param label Label function to use. +-- @param buttons A table with buttons binding to set. +function tasklist.new(label, buttons) + local w = {} + local function tasklist_update () + local clients = capi.client.get() + for k, c in ipairs(clients) do + if c.skip_taskbar or c.hide + or c.type == "splash" or c.type == "dock" or c.type == "desktop" then + table.remove(clients, k) + end + end + -- Hack: if it has been registered as a widget in a wibox, + -- it's w.len since __len meta does not work on table until Lua 5.2. + -- Otherwise it's standard #w. + local len = (w.len or #w) / 2 + -- Add more widgets + if len < #clients then + for i = len * 2 + 1, #clients * 2, 2 do + w[i] = capi.widget({ type = "imagebox", name = "tasklist_icon" .. i, align = "flex" }) + w[i + 1] = capi.widget({ type = "textbox", name = "tasklist_text" .. i, align = "flex" }) + end + -- Remove widgets + elseif len > #clients then + for i = #clients * 2 + 1, len * 2, 2 do + w[i] = nil + w[i + 1] = nil + end + end + -- Update widgets text + for k = 1, #clients * 2, 2 do + if buttons then + -- Replace press function by a new one calling with tags as + -- argument + local mbuttons = {} + for kb, b in ipairs(buttons) do + -- Copy object + mbuttons[kb] = capi.button(b) + mbuttons[kb].press = function () b.press(clients[(k + 1) / 2]) end + end + w[k]:buttons(mbuttons) + w[k + 1]:buttons(mbuttons) + end + w[k + 1].text, w[k].bg = label(clients[(k + 1) / 2]) + if w[k + 1].text then + w[k].visible = true + w[k + 1].visible = true + w[k].image = clients[(k + 1) / 2].icon + else + w[k].visible = false + w[k + 1].visible = false + end + end + end + hooks.clients.register(tasklist_update) + hooks.tagged.register(tasklist_update) + hooks.focus.register(tasklist_update) + hooks.unfocus.register(tasklist_update) + hooks.property.register(function (c, prop) + if prop == "urgent" + or prop == "floating" + or prop == "icon" then + tasklist_update() + end + end) + tasklist_update() + return w +end + local function widget_tasklist_label_common(c, args) if not args then args = {} end local theme = beautiful.get() @@ -142,6 +268,7 @@ local function widget_tasklist_label_common(c, args) local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent + local bg = nil local text = "" local name if c.floating then @@ -154,16 +281,18 @@ local function widget_tasklist_label_common(c, args) end if capi.client.focus == c then if bg_focus and fg_focus then + bg = bg_focus text = text .. ""..name.."" else text = text .. name end elseif c.urgent and bg_urgent and fg_urgent then + local bg = bg_urgent text = text .. ""..name.."" else text = text .. name end - return text + return text, bg end --- Return labels for a tasklist widget with clients from all tags and screen. diff --git a/lib/invaders.lua.in b/lib/invaders.lua.in index 9b947f913..85ea7fd3d 100644 --- a/lib/invaders.lua.in +++ b/lib/invaders.lua.in @@ -69,7 +69,7 @@ function player.new () w = widget({ type = "imagebox", name = "player" }) w.image = image("@AWESOME_ICON_PATH@/invaders/player.png") - p:widgets({ w }) + p.widgets = w return p end @@ -177,7 +177,7 @@ function enemies.new (t) e.screen = 1 w = widget({ type = "imagebox", name = "enemy"..t }) w.image = image("@AWESOME_ICON_PATH@/invaders/enemy_"..t..".png") - e:widgets({ w }) + e.widgets = w return e end @@ -328,7 +328,7 @@ function game.quit() if gamedata.highscore.window then gamedata.highscore.window.screen = nil - gamedata.highscore.window:widgets({ }) + gamedata.highscore.window.widgets = nil end if gamedata.field.background then @@ -336,7 +336,7 @@ function game.quit() end gamedata.player.screen = nil - gamedata.player:widgets({ }) + gamedata.player.widgets = nil gamedata.player = nil gamedata.field.north.screen = nil @@ -354,7 +354,7 @@ function game.quit() for y = 1, #gamedata.enemies.data do for x = 1, #gamedata.enemies.data[y] do gamedata.enemies.data[y][x].screen = nil - gamedata.enemies.data[y][x]:widgets({ }) + gamedata.enemies.data[y][x].widgets = nil end end @@ -374,7 +374,7 @@ function game.highscore_show () gamedata.highscore.window.screen = 1 gamedata.highscore.table = widget({ type = "textbox", name = "highscore" }) - gamedata.highscore.window:widgets({ gamedata.highscore.table }) + gamedata.highscore.window.widgets = gamedata.highscore.table gamedata.highscore.table.text = " Highscores:\n" @@ -440,7 +440,7 @@ function game.highscore (score) gamedata.namebox = widget({ type = "textbox", name = "foobar" }) gamedata.namebox.text = " Name: |" - gamedata.highscore.window:widgets({ gamedata.namebox }) + gamedata.highscore.window.widgets = gamedata.namebox if newentry then gamedata.name = "" @@ -493,7 +493,7 @@ function run(args) gamedata.field.caption = widget({ type = "textbox", name = "caption", align = "left" }) gamedata.field.caption.text = " Awesome Invaders" - gamedata.field.north:widgets({ gamedata.field.caption, gamedata.field.status }) + gamedata.field.north.widgets = { gamedata.field.caption, gamedata.field.status } gamedata.field.south = wibox({ position = "floating", bg = gamedata.btheme.bg_focus or "#333333", diff --git a/lib/naughty.lua.in b/lib/naughty.lua.in index 83869189a..8a9fe4daa 100644 --- a/lib/naughty.lua.in +++ b/lib/naughty.lua.in @@ -217,7 +217,7 @@ function notify(args) iconbox.width = 20 end - box:widgets({ iconbox, textbox }) + box.widgets = { iconbox, textbox } local timer = function () destroy(notification) end hooks.timer.register(timeout, timer) diff --git a/luaa.c b/luaa.c index eee436aa7..354ed3f28 100644 --- a/luaa.c +++ b/luaa.c @@ -231,6 +231,32 @@ luaA_hooks_clients(lua_State *L) return luaA_registerfct(L, 1, &globalconf.hooks.clients); } +/** Set the function called on each screen tag list change. + * This function is called with a screen number as argument. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + * \lparam A function to call on each tag list change. + */ +static int +luaA_hooks_tags(lua_State *L) +{ + return luaA_registerfct(L, 1, &globalconf.hooks.tags); +} + +/** Set the function called on each client's tags change. + * This function is called with the client and the tag as argument. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + * \lparam A function to call on each client's tags change. + */ +static int +luaA_hooks_tagged(lua_State *L) +{ + return luaA_registerfct(L, 1, &globalconf.hooks.tagged); +} + /** Set the function called on each screen arrange. This function is called * with the screen number as argument. * \param L The Lua VM state. @@ -397,6 +423,10 @@ luaA_openlib(lua_State *L, const char *name, lua_pop(L, 2); } +/** UTF-8 aware string length computing. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ static int luaA_mbstrlen(lua_State *L) { @@ -405,8 +435,12 @@ luaA_mbstrlen(lua_State *L) return 1; } +/** Overload standard Lua next function to use __next key on metatable. + * \param L The Lua VM state. + * \param The number of elements pushed on stack. + */ static int -luaA_next(lua_State *L) +luaAe_next(lua_State *L) { if(luaL_getmetafield(L, 1, "__next")) { @@ -423,8 +457,56 @@ luaA_next(lua_State *L) return 1; } +/** Overload lua_next() function by using __next metatable field + * to get next elements. + * \param L The Lua VM stack. + * \param idx The index number of elements in stack. + * \return 1 if more elements to come, 0 otherwise. + */ +int +luaA_next(lua_State *L, int idx) +{ + if(luaL_getmetafield(L, idx, "__next")) + { + /* if idx is relative, reduce it since we got __next */ + if(idx < 0) idx--; + /* copy table and then move key */ + lua_pushvalue(L, idx); + lua_pushvalue(L, -3); + lua_remove(L, -4); + lua_pcall(L, 2, 2, 0); + /* next returned nil, it's the end */ + if(lua_isnil(L, -1)) + { + /* remove nil */ + lua_pop(L, 2); + return 0; + } + return 1; + } + + return lua_next(L, idx); +} + +/** Generic pairs function. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ static int -luaA_pairs(lua_State *L) +luaA_generic_pairs(lua_State *L) +{ + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + +/** Overload standard pairs function to use __pairs field of metatables. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +static int +luaAe_pairs(lua_State *L) { if(luaL_getmetafield(L, 1, "__pairs")) { @@ -448,14 +530,170 @@ luaA_fixups(lua_State *L) lua_setfield(L, -2, "len"); lua_pop(L, 1); lua_pushliteral(L, "next"); - lua_pushcfunction(L, luaA_next); + lua_pushcfunction(L, luaAe_next); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "pairs"); - lua_pushcfunction(L, luaA_next); - lua_pushcclosure(L, luaA_pairs, 1); /* pairs get next as upvalue */ + lua_pushcfunction(L, luaAe_next); + lua_pushcclosure(L, luaAe_pairs, 1); /* pairs get next as upvalue */ lua_settable(L, LUA_GLOBALSINDEX); } +/** __next function for wtable objects. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +static int +luaA_wtable_next(lua_State *L) +{ + /* upvalue 1 is content table */ + if(lua_next(L, lua_upvalueindex(1))) + return 2; + lua_pushnil(L); + return 1; +} + +/** Index function of wtable objects. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +static int +luaA_wtable_index(lua_State *L) +{ + size_t len; + const char *buf; + + lua_pushvalue(L, 2); + /* check for size, waiting lua 5.2 and __len on tables */ + if((buf = lua_tolstring(L, -1, &len))) + if(a_tokenize(buf, len) == A_TK_LEN) + { + lua_pushnumber(L, lua_objlen(L, lua_upvalueindex(1))); + return 1; + } + lua_pop(L, 1); + + /* upvalue 1 is content table */ + lua_rawget(L, lua_upvalueindex(1)); + return 1; +} + +/** Newndex function of wtable objects. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +static int +luaA_wtable_newindex(lua_State *L) +{ + bool invalid = false; + + /* push key on top */ + lua_pushvalue(L, 2); + /* get current key value in content table */ + lua_rawget(L, lua_upvalueindex(1)); + /* if value is a widget, notify change */ + if(lua_istable(L, -1) || luaA_toudata(L, -1, "widget")) + invalid = true; + + lua_pop(L, 1); /* remove value */ + + /* if new value is a widget or a table */ + if(lua_istable(L, 3)) + { + luaA_table2wtable(L); + invalid = true; + } + else if(!invalid && luaA_toudata(L, 3, "widget")) + invalid = true; + + /* upvalue 1 is content table */ + lua_rawset(L, lua_upvalueindex(1)); + + if(invalid) + luaA_wibox_invalidate_byitem(L, lua_topointer(L, 1)); + + return 0; +} + +/** Convert the top element of the stack to a proxied wtable. + * \param L The Lua VM state. + */ +void +luaA_table2wtable(lua_State *L) +{ + if(!lua_istable(L, -1)) + return; + + lua_newtable(L); /* create *real* content table */ + lua_newtable(L); /* metatable */ + lua_pushvalue(L, -2); /* copy content table */ + lua_pushcclosure(L, luaA_wtable_next, 1); /* __next has the content table as upvalue */ + lua_pushvalue(L, -3); /* copy content table */ + lua_pushcclosure(L, luaA_wtable_index, 1); /* __index has the content table as upvalue */ + lua_pushvalue(L, -4); /* copy content table */ + lua_pushcclosure(L, luaA_wtable_newindex, 1); /* __newindex has the content table as upvalue */ + /* set metatable field with just pushed closure */ + lua_setfield(L, -4, "__newindex"); + lua_setfield(L, -3, "__index"); + lua_setfield(L, -2, "__next"); + /* set metatable impossible to touch */ + lua_pushliteral(L, "wtable"); + lua_setfield(L, -2, "__metatable"); + /* set new metatable on original table */ + lua_setmetatable(L, -3); + + /* initial key */ + lua_pushnil(L); + /* go through original table */ + while(lua_next(L, -3)) + { + /* if convert value to wtable */ + luaA_table2wtable(L); + /* copy key */ + lua_pushvalue(L, -2); + /* move key before value */ + lua_insert(L, -2); + /* set same value in content table */ + lua_rawset(L, -4); + /* copy key */ + lua_pushvalue(L, -1); + /* push the new value :-> */ + lua_pushnil(L); + /* set orig[k] = nil */ + lua_rawset(L, -5); + } + /* remove content table */ + lua_pop(L, 1); +} + +/** Look for an item: table, function, etc. + * \param L The Lua VM state. + * \param item The pointer item. + */ +bool +luaA_hasitem(lua_State *L, const void *item) +{ + lua_pushnil(L); + while(luaA_next(L, -2)) + { + if(lua_topointer(L, -1) == item) + { + /* remove value and key */ + lua_pop(L, 2); + return true; + } + if(lua_istable(L, -1)) + if(luaA_hasitem(L, item)) + { + /* remove key and value */ + lua_pop(L, 2); + return true; + } + /* remove value */ + lua_pop(L, 1); + } + return false; +} + /** Object table. * This table can use safely object as key. * \param L The Lua VM state. @@ -625,6 +863,8 @@ luaA_init(void) { "property", luaA_hooks_property }, { "arrange", luaA_hooks_arrange }, { "clients", luaA_hooks_clients }, + { "tags", luaA_hooks_tags }, + { "tagged", luaA_hooks_tagged }, { "timer", luaA_hooks_timer }, /* deprecated */ { "mouse_over", luaA_hooks_mouse_over }, @@ -701,6 +941,8 @@ luaA_init(void) globalconf.hooks.mouse_enter = LUA_REFNIL; globalconf.hooks.arrange = LUA_REFNIL; globalconf.hooks.clients = LUA_REFNIL; + globalconf.hooks.tags = LUA_REFNIL; + globalconf.hooks.tagged = LUA_REFNIL; globalconf.hooks.property = LUA_REFNIL; globalconf.hooks.timer = LUA_REFNIL; } @@ -1001,7 +1243,12 @@ luaA_on_timer(EV_P_ ev_timer *w, int revents) awesome_refresh(globalconf.connection); } -void +/** Push a color as a string onto the stack + * \param L The Lua VM state. + * \param c The color to push. + * \return The number of elements pushed on stack. + */ +int luaA_pushcolor(lua_State *L, const xcolor_t *c) { uint8_t r = (unsigned)c->red * 0xff / 0xffff; @@ -1015,4 +1262,5 @@ luaA_pushcolor(lua_State *L, const xcolor_t *c) else snprintf(s, sizeof(s), "#%02x%02x%02x%02x", r, g, b, a); lua_pushlstring(L, s, sizeof(s)); + return 1; } diff --git a/luaa.h b/luaa.h index 124665a98..6e8238c32 100644 --- a/luaa.h +++ b/luaa.h @@ -333,17 +333,11 @@ void luaA_init(void); bool luaA_parserc(const char *, bool); void luaA_cs_init(void); void luaA_cs_cleanup(void); -void luaA_on_timer(EV_P_ ev_timer *w, int revents); -void luaA_pushcolor(lua_State *, const xcolor_t *); - -static inline int -luaA_generic_pairs(lua_State *L) -{ - lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ - return 3; -} +void luaA_on_timer(EV_P_ ev_timer *, int); +int luaA_pushcolor(lua_State *, const xcolor_t *); +bool luaA_hasitem(lua_State *, const void *); +void luaA_table2wtable(lua_State *); +int luaA_next(lua_State *, int); #define hooks_property(c, prop) \ do { \ diff --git a/property.c b/property.c index 6f21fabbf..ccc77bfc2 100644 --- a/property.c +++ b/property.c @@ -187,11 +187,8 @@ property_update_wm_hints(client_t *c, xcb_get_property_reply_t *reply) if(isurgent != c->isurgent) { c->isurgent = isurgent; - /* execute hook */ hooks_property(c, "urgent"); - - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); } if(wmh.flags & XCB_WM_HINT_STATE && wmh.initial_state == XCB_WM_STATE_WITHDRAWN) @@ -240,8 +237,6 @@ property_update_wm_name(client_t *c) /* call hook */ hooks_property(c, "name"); - - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); } static int @@ -290,7 +285,6 @@ property_handle_net_wm_icon(void *data, { image_t *icon; image_unref(&c->icon); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); icon = ewmh_window_icon_from_reply(reply); c->icon = icon ? image_ref(&icon) : NULL; diff --git a/screen.c b/screen.c index 22826b785..1f7afe6c4 100644 --- a/screen.c +++ b/screen.c @@ -315,9 +315,6 @@ screen_client_moveto(client_t *c, int new_screen, bool dotag, bool doresize) if(c->titlebar) c->titlebar->screen = new_screen; - widget_invalidate_cache(old_screen, WIDGET_CACHE_CLIENTS); - widget_invalidate_cache(new_screen, WIDGET_CACHE_CLIENTS); - if(dotag && !c->issticky) { /* remove old tags */ diff --git a/structs.h b/structs.h index 5de9a4b4d..45f6cd50c 100644 --- a/structs.h +++ b/structs.h @@ -105,6 +105,7 @@ typedef struct int screen; /** Widget list */ widget_node_t *widgets; + luaA_ref widgets_table; /** Widget the mouse is over */ widget_node_t *mouse_over; /** Need update */ @@ -141,8 +142,6 @@ struct widget_t widget_constructor_t *type; /** Widget destructor */ widget_destructor_t *destructor; - /** Widget detach function */ - void (*detach)(widget_t *, wibox_t *); /** Draw function */ int (*draw)(draw_context_t *, int, widget_node_t *, int, int, wibox_t *); /** Index function */ @@ -437,6 +436,10 @@ struct awesome_t luaA_ref arrange; /** Command to run when client list changes */ luaA_ref clients; + /** Command to run on numbers of tag changes */ + luaA_ref tags; + /** Command to run when client gets (un)tagged */ + luaA_ref tagged; /** Command to run on property change */ luaA_ref property; /** Command to run on time */ diff --git a/tag.c b/tag.c index ae45cebb7..3d09f6eba 100644 --- a/tag.c +++ b/tag.c @@ -42,7 +42,6 @@ tag_view(tag_t *tag, bool view) { tag->selected = view; ewmh_update_net_current_desktop(screen_virttophys(tag->screen)); - widget_invalidate_cache(tag->screen, WIDGET_CACHE_TAGS); globalconf.screens[tag->screen].need_arrange = true; } @@ -94,7 +93,9 @@ tag_append_to_screen(tag_t *tag, screen_t *s) ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); - widget_invalidate_cache(s->index, WIDGET_CACHE_TAGS); + /* call hook */ + lua_pushnumber(globalconf.L, s->index+ 1); + luaA_dofunction(globalconf.L, globalconf.hooks.tags, 1, 0); } /** Remove a tag from screen. Tag must be on a screen and have no clients. @@ -103,6 +104,7 @@ tag_append_to_screen(tag_t *tag, screen_t *s) 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; @@ -115,9 +117,11 @@ tag_remove_from_screen(tag_t *tag) ewmh_update_net_numbers_of_desktop(phys_screen); ewmh_update_net_desktop_names(phys_screen); ewmh_update_workarea(phys_screen); - widget_invalidate_cache(tag->screen, WIDGET_CACHE_TAGS); tag->screen = SCREEN_UNDEF; tag_unref(&tag); + /* call hook */ + lua_pushnumber(globalconf.L, screen + 1); + luaA_dofunction(globalconf.L, globalconf.hooks.tags, 1, 0); } /** Tag a client with specified tag. @@ -134,8 +138,10 @@ tag_client(client_t *c, tag_t *t) tag_ref(&t); client_array_append(&t->clients, c); client_saveprops_tags(c); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); client_need_arrange(c); + /* call hook */ + luaA_client_userdata_new(globalconf.L, c); + luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 1, 0); } /** Untag a client with specified tag. @@ -152,7 +158,10 @@ untag_client(client_t *c, tag_t *t) client_array_take(&t->clients, i); tag_unref(&t); client_saveprops_tags(c); - widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); + /* call hook */ + luaA_client_userdata_new(globalconf.L, c); + luaA_tag_userdata_new(globalconf.L, t); + luaA_dofunction(globalconf.L, globalconf.hooks.tagged, 2, 0); return; } } @@ -404,12 +413,9 @@ luaA_tag_newindex(lua_State *L) case A_TK_NAME: buf = luaL_checklstring(L, 3, &len); if((*tag)->screen != SCREEN_UNDEF) - { if(tag_getbyname((*tag)->screen, (*tag)->name) != *tag) luaL_error(L, "a tag with the name `%s' is already on screen %d", buf, (*tag)->screen); - widget_invalidate_cache((*tag)->screen, WIDGET_CACHE_TAGS); - } p_delete(&(*tag)->name); a_iso2utf8(&(*tag)->name, buf, len); break; diff --git a/wibox.c b/wibox.c index 2eafacee8..5ec42d6f5 100644 --- a/wibox.c +++ b/wibox.c @@ -439,6 +439,18 @@ wibox_position_update(wibox_t *wibox) wibox_move(wibox, wingeom.x, wingeom.y); } +/** Delete a wibox. + * \param wibox wibox to delete. + */ +void +wibox_delete(wibox_t **wibox) +{ + simplewindow_wipe(&(*wibox)->sw); + luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*wibox)->widgets_table); + widget_node_list_wipe(&(*wibox)->widgets); + p_delete(wibox); +} + /** Get a wibox by its window. * \param w The window id. * \return A wibox if found, NULL otherwise. @@ -627,6 +639,7 @@ luaA_wibox_new(lua_State *L) luaA_checktable(L, 2); w = p_new(wibox_t, 1); + w->widgets_table = LUA_REFNIL; w->sw.ctx.fg = globalconf.colors.fg; if((buf = luaA_getopt_lstring(L, 2, "fg", NULL, &len))) @@ -677,6 +690,64 @@ luaA_wibox_new(lua_State *L) return luaA_wibox_userdata_new(L, w); } +/** Rebuild wibox widgets list. + * \param L The Lua VM state. + * \param wibox The wibox. + */ +static void +wibox_widgets_table_build(lua_State *L, wibox_t *wibox) +{ + widget_node_list_wipe(&wibox->widgets); + luaA_table2widgets(L, &wibox->widgets); + wibox->mouse_over = NULL; + wibox->need_update = true; +} + +/** Check if a wibox widget table has an item. + * \param L The Lua VM state. + * \param wibox The wibox. + * \param item The item to look for. + */ +static bool +luaA_wibox_hasitem(lua_State *L, wibox_t *wibox, const void *item) +{ + bool ret = false; + lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, wibox->widgets_table); + if(lua_topointer(L, -1) == item || luaA_hasitem(L, item)) + ret = true; + return ret; +} + +/** Invalidate a wibox by a Lua object (table, etc). + * \param L The Lua VM state. + * \param item The object identifier. + */ +void +luaA_wibox_invalidate_byitem(lua_State *L, const void *item) +{ + for(int screen = 0; screen < globalconf.nscreen; screen++) + for(int i = 0; i < globalconf.screens[screen].wiboxes.len; i++) + { + wibox_t *wibox = globalconf.screens[screen].wiboxes.tab[i]; + if(luaA_wibox_hasitem(L, wibox, item)) + { + /* recompute widget node list */ + wibox_widgets_table_build(L, wibox); + lua_pop(L, 1); /* remove widgets table */ + } + + } + + for(client_t *c = globalconf.clients; c; c = c->next) + if(c->titlebar && luaA_wibox_hasitem(L, c->titlebar, item)) + { + /* recompute widget node list */ + wibox_widgets_table_build(L, c->titlebar); + lua_pop(L, 1); /* remove widgets table */ + } +} + + /** Wibox object. * \param L The Lua VM state. * \return The number of elements pushed on stack. @@ -742,6 +813,9 @@ luaA_wibox_index(lua_State *L) case A_TK_ORIENTATION: lua_pushstring(L, orientation_tostr((*wibox)->sw.orientation)); break; + case A_TK_WIDGETS: + lua_rawgeti(L, LUA_REGISTRYINDEX, (*wibox)->widgets_table); + break; default: return 0; } @@ -749,88 +823,6 @@ luaA_wibox_index(lua_State *L) return 1; } -/** Generic widget set. - * \param L The Lua VM state. - * \param idx The table of widgets index. - * \param object The object the widget will be attached to. - * \param widgets The widgets to fill. - * \return The number of elements pushed on stack. - */ -static void -luaA_widget_set(lua_State *L, int idx, wibox_t *object, widget_node_t **widgets) -{ - widget_node_t *witer; - - luaA_checktable(L, idx); - - /* remove all widgets */ - for(witer = *widgets; witer; witer = *widgets) - { - if(witer->widget->detach) - witer->widget->detach(witer->widget, object); - widget_unref(&witer->widget); - widget_node_list_detach(widgets, witer); - p_delete(&witer); - } - - /* now read all widgets and add them */ - lua_pushnil(L); - while(lua_next(L, idx)) - { - widget_t **widget = luaA_checkudata(L, -1, "widget"); - widget_node_t *w = p_new(widget_node_t, 1); - w->widget = *widget; - widget_node_list_append(widgets, w); - widget_ref(widget); - lua_pop(L, 1); - } -} - -/** Generic widget get. - * \param L The Lua VM state. - * \param widget The widget list. - * \return The number of elements pushed on stack. - */ -static int -luaA_widget_get(lua_State *L, widget_node_t *widgets) -{ - widget_node_t *witer; - int i = 0; - - lua_newtable(L); - - for(witer = widgets; witer; witer = witer->next) - { - luaA_widget_userdata_new(L, witer->widget); - lua_rawseti(L, -2, ++i); - } - - return 1; -} - - -/** Get or set the wibox widgets. - * \param L The Lua VM state. - * \return The number of elements pushed on stack. - * \luastack - * \lparam None, or a table of widgets to set. - * \lreturn The current wibox widgets. -*/ -static int -luaA_wibox_widgets(lua_State *L) -{ - wibox_t **wibox = luaA_checkudata(L, 1, "wibox"); - - if(lua_gettop(L) == 2) - { - luaA_widget_set(L, 2, *wibox, &(*wibox)->widgets); - (*wibox)->need_update = true; - (*wibox)->mouse_over = NULL; - return 1; - } - return luaA_widget_get(L, (*wibox)->widgets); -} - /* Set or get the wibox geometry. * \param L The Lua VM state. * \return The number of elements pushed on stack. @@ -986,6 +978,12 @@ luaA_wibox_newindex(lua_State *L) break; } break; + case A_TK_WIDGETS: + luaA_register(L, 3, &(*wibox)->widgets_table); + /* recompute widget node list */ + wibox_widgets_table_build(L, *wibox); + luaA_table2wtable(L); + break; default: switch((*wibox)->type) { @@ -1006,7 +1004,6 @@ const struct luaL_reg awesome_wibox_methods[] = }; const struct luaL_reg awesome_wibox_meta[] = { - { "widgets", luaA_wibox_widgets }, { "geometry", luaA_wibox_geometry }, { "__index", luaA_wibox_index }, { "__newindex", luaA_wibox_newindex }, diff --git a/wibox.h b/wibox.h index d736c7e9b..50ab4d6d1 100644 --- a/wibox.h +++ b/wibox.h @@ -32,18 +32,13 @@ void wibox_refresh(void); int luaA_wibox_new(lua_State *); int luaA_wibox_userdata_new(lua_State *, wibox_t *); +void luaA_wibox_invalidate_byitem(lua_State *, const void *); + void wibox_position_update(wibox_t *); wibox_t * wibox_getbywin(xcb_window_t); void wibox_detach(wibox_t *); void wibox_attach(wibox_t *, screen_t *); - -static inline void -wibox_delete(wibox_t **wibox) -{ - simplewindow_wipe(&(*wibox)->sw); - widget_node_list_wipe(&(*wibox)->widgets); - p_delete(wibox); -} +void wibox_delete(wibox_t **); static inline void wibox_moveresize(wibox_t *wibox, area_t geometry) diff --git a/widget.c b/widget.c index a7b6428fa..c8ce6a42a 100644 --- a/widget.c +++ b/widget.c @@ -82,6 +82,34 @@ widget_common_button(widget_node_t *w, } } +/** Convert a Lua table to a list of widget nodet. + * \param L The Lua VM state. + * \param widgets The linked list of widget node. + */ +void +luaA_table2widgets(lua_State *L, widget_node_t **widgets) +{ + if(lua_istable(L, -1)) + { + lua_pushnil(L); + while(luaA_next(L, -2)) + { + luaA_table2widgets(L, widgets); + lua_pop(L, 1); /* remove value */ + } + } + else + { + widget_t **widget = luaA_toudata(L, -1, "widget"); + if(widget) + { + widget_node_t *w = p_new(widget_node_t, 1); + w->widget = widget_ref(widget); + widget_node_list_append(widgets, w); + } + } +} + /** Render a list of widgets. * \param wnode The list of widgets. * \param ctx The draw context where to render. @@ -91,13 +119,13 @@ widget_common_button(widget_node_t *w, * \param orientation The object orientation. * \param x The x coordinates of the object. * \param y The y coordinates of the object. - * \param object The wibox. + * \param wibox The wibox. * \todo Remove GC. */ void widget_render(widget_node_t *wnode, draw_context_t *ctx, xcb_gcontext_t gc, xcb_pixmap_t rotate_px, int screen, orientation_t orientation, - int x, int y, wibox_t *object) + int x, int y, wibox_t *wibox) { xcb_pixmap_t rootpix; xcb_screen_t *s; @@ -157,16 +185,27 @@ widget_render(widget_node_t *wnode, draw_context_t *ctx, xcb_gcontext_t gc, xcb_ for(w = wnode; w; w = w->next) if(w->widget->align == AlignLeft && w->widget->isvisible) - left += w->widget->draw(ctx, screen, w, left, (left + right), object); + left += w->widget->draw(ctx, screen, w, left, (left + right), wibox); /* renders right widget from last to first */ for(w = *widget_node_list_last(&wnode); w; w = w->prev) if(w->widget->align == AlignRight && w->widget->isvisible) - right += w->widget->draw(ctx, screen, w, right, (left + right), object); + right += w->widget->draw(ctx, screen, w, right, (left + right), wibox); + /* \todo rewrite this */ + int flex = 0; for(w = wnode; w; w = w->next) if(w->widget->align == AlignFlex && w->widget->isvisible) - left += w->widget->draw(ctx, screen, w, left, (left + right), object); + flex++; + + if(flex) + { + int length = (ctx->width - (left + right)) / flex; + + for(w = wnode; w; w = w->next) + if(w->widget->align == AlignFlex && w->widget->isvisible) + left += w->widget->draw(ctx, screen, w, left, (left + right) + length * --flex , wibox); + } switch(orientation) { @@ -228,29 +267,27 @@ widget_invalidate_cache(int screen, int flags) void widget_invalidate_bywidget(widget_t *widget) { - int screen; - widget_node_t *witer; - client_t *c; - - for(screen = 0; screen < globalconf.nscreen; screen++) + for(int screen = 0; screen < globalconf.nscreen; screen++) for(int i = 0; i < globalconf.screens[screen].wiboxes.len; i++) { wibox_t *wibox = globalconf.screens[screen].wiboxes.tab[i]; - if(!wibox->need_update) - for(witer = wibox->widgets; witer; witer = witer->next) + for(widget_node_t *witer = wibox->widgets; witer; witer = witer->next) if(witer->widget == widget) { wibox->need_update = true; break; } - } + } - for(c = globalconf.clients; c; c = c->next) + for(client_t *c = globalconf.clients; c; c = c->next) if(c->titlebar && !c->titlebar->need_update) - for(witer = c->titlebar->widgets; witer; witer = witer->next) + for(widget_node_t *witer = c->titlebar->widgets; witer; witer = witer->next) if(witer->widget == widget) + { c->titlebar->need_update = true; + break; + } } /** Create a new widget. diff --git a/widget.h b/widget.h index 320b7d82f..d886b8570 100644 --- a/widget.h +++ b/widget.h @@ -24,8 +24,6 @@ #include "structs.h" -#define WIDGET_CACHE_CLIENTS (1<<0) -#define WIDGET_CACHE_TAGS (1<<2) #define WIDGET_CACHE_EMBEDDED (1<<3) void widget_invalidate_cache(int, int); @@ -34,14 +32,13 @@ void widget_common_new(widget_t *); void widget_render(widget_node_t *, draw_context_t *, xcb_gcontext_t, xcb_drawable_t, int, orientation_t, int, int, wibox_t *); int luaA_widget_userdata_new(lua_State *, widget_t *); +void luaA_table2widgets(lua_State *, widget_node_t **); void widget_invalidate_bywidget(widget_t *); -widget_constructor_t taglist_new; widget_constructor_t textbox_new; widget_constructor_t progressbar_new; widget_constructor_t graph_new; -widget_constructor_t tasklist_new; widget_constructor_t systray_new; widget_constructor_t imagebox_new; diff --git a/widgets/taglist.c b/widgets/taglist.c deleted file mode 100644 index 9ecb5d03f..000000000 --- a/widgets/taglist.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * taglist.c - tag list widget - * - * 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 "client.h" -#include "widget.h" -#include "tag.h" -#include "wibox.h" -#include "common/tokenize.h" - -extern awesome_t globalconf; - -/** Data type used to store where we draw the taglist for a particular object. - * This is filled in the draw function, and use later when we get a click. - */ -typedef struct taglist_drawn_area_t taglist_drawn_area_t; -struct taglist_drawn_area_t -{ - wibox_t *object; - area_array_t areas; - taglist_drawn_area_t *next, *prev; -}; - -/** Delete a taglist_drawn_area_t object. - * \param a The drawn area to delete. - */ -static void -taglist_drawn_area_delete(taglist_drawn_area_t **a) -{ - area_array_wipe(&(*a)->areas); - p_delete(a); -} - -DO_SLIST(taglist_drawn_area_t, taglist_drawn_area, taglist_drawn_area_delete); - -/** Taglist widget private data */ -typedef struct -{ - taglist_drawn_area_t *drawn_area; - luaA_ref label; -} taglist_data_t; - -static taglist_drawn_area_t * -taglist_drawn_area_getbyobj(taglist_drawn_area_t *list, wibox_t *p) -{ - taglist_drawn_area_t *t; - - for(t = list; t; t = t->next) - if(t->object == p) - return t; - - return NULL; -} - -/** Draw a taglist. - * \param ctx The draw context. - * \param screen The screen we're drawing for. - * \param w The widget node we're drawing for. - * \param offset Offset to draw at. - * \param used Space already used. - * \param object The object pointer we're drawing onto. - * \return The width used. - */ -static int -taglist_draw(draw_context_t *ctx, int screen, widget_node_t *w, - int offset, - int used __attribute__ ((unused)), - wibox_t *object) -{ - taglist_data_t *data = w->widget->data; - area_t area; - taglist_drawn_area_t *tda; - int prev_width = 0; - - tag_array_t *tags = &globalconf.screens[screen].tags; - const char **text = p_alloca(const char *, tags->len); - size_t *len = p_alloca(size_t, tags->len); - draw_parser_data_t *pdata = p_alloca(draw_parser_data_t, tags->len); - - w->area.width = w->area.y = 0; - - /* Lookup for our taglist_drawn_area. - * This will be used to store area where we draw tag list for each object. */ - if(!(tda = taglist_drawn_area_getbyobj(data->drawn_area, object))) - { - /* Oh, we did not find a drawn area for our object. First time? */ - tda = p_new(taglist_drawn_area_t, 1); - tda->object = object; - taglist_drawn_area_list_push(&data->drawn_area, tda); - } - - area_array_wipe(&tda->areas); - area_array_init(&tda->areas); - - /* First compute text and widget width */ - for(int i = 0; i < tags->len; i++) - { - tag_t *tag = tags->tab[i]; - p_clear(&area, 1); - - luaA_tag_userdata_new(globalconf.L, tag); - if(luaA_dofunction(globalconf.L, data->label, 1, 1)) - { - if(lua_isstring(globalconf.L, -1)) - text[i] = lua_tolstring(globalconf.L, -1, &len[i]); - - lua_pop(globalconf.L, 1); - - draw_parser_data_init(&pdata[i]); - area = draw_text_extents(ctx->phys_screen, - globalconf.font, text[i], len[i], &pdata[i]); - area.height = ctx->height; - - if(pdata[i].bg_image) - area.width = MAX(area.width, - pdata[i].bg_resize ? ((double) pdata[i].bg_image->width / (double) pdata[i].bg_image->height) * w->area.height : pdata[i].bg_image->width); - - w->area.width += area.width; - } - area_array_append(&tda->areas, area); - } - - /* Now that we have widget width we can compute widget x coordinate */ - w->area.x = widget_calculate_offset(ctx->width, w->area.width, - offset, w->widget->align); - - for(int i = 0; i < tags->len; i++) - { - area_t *r = &tda->areas.tab[i]; - - r->x = w->area.x + prev_width; - prev_width += r->width; - draw_text(ctx, globalconf.font, *r, text[i], len[i], &pdata[i]); - draw_parser_data_wipe(&pdata[i]); - } - - w->area.height = ctx->height; - return w->area.width; -} - -/** Handle button click on tasklist. - * \param w The widget node. - * \param ev The button press event. - * \param btype The button press event type. - * \param screen The screen where the click was. - * \param object The object we're onto. - */ -static void -taglist_button(widget_node_t *w, - xcb_button_press_event_t *ev, - int screen, - wibox_t *object) -{ - tag_array_t *tags = &globalconf.screens[screen].tags; - taglist_data_t *data = w->widget->data; - taglist_drawn_area_t *tda; - button_array_t *barr = &w->widget->buttons; - - /* Find the good drawn area list */ - if((tda = taglist_drawn_area_getbyobj(data->drawn_area, object))) - for(int i = 0; i < barr->len; i++) - if(ev->detail == barr->tab[i]->button - && XUTIL_MASK_CLEAN(ev->state) == barr->tab[i]->mod) - for(int j = 0; i < MIN(tags->len, tda->areas.len); j++) - { - tag_t *tag = tags->tab[j]; - area_t *area = &tda->areas.tab[j]; - if(ev->event_x >= AREA_LEFT(*area) - && ev->event_x < AREA_RIGHT(*area)) - { - luaA_wibox_userdata_new(globalconf.L, object); - luaA_tag_userdata_new(globalconf.L, tag); - luaA_dofunction(globalconf.L, - ev->response_type == XCB_BUTTON_PRESS ? - barr->tab[i]->press : barr->tab[i]->release, - 2, 0); - return; - } - } -} - -/** Taglist widget. - * \param L The Lua VM state. - * \param token The key token. - * \return The number of elements pushed on stack. - * \luastack - * \lfield label Function used to get the string to display as the tag title. - * It gets the tag as argument, and must return a string. - */ -static int -luaA_taglist_index(lua_State *L, awesome_token_t token) -{ - widget_t **widget = luaA_checkudata(L, 1, "widget"); - taglist_data_t *d = (*widget)->data; - - switch(token) - { - case A_TK_LABEL: - lua_rawgeti(L, LUA_REGISTRYINDEX, d->label); - return 1; - default: - return 0; - } -} - -/** Newindex function for taglist. - * \param L The Lua VM state. - * \param token The key token. - * \return The number of elements pushed on stack. - */ -static int -luaA_taglist_newindex(lua_State *L, awesome_token_t token) -{ - widget_t **widget = luaA_checkudata(L, 1, "widget"); - taglist_data_t *d = (*widget)->data; - - switch(token) - { - case A_TK_LABEL: - luaA_registerfct(L, 3, &d->label); - break; - default: - return 0; - } - - widget_invalidate_bywidget(*widget); - - return 0; -} - -/** Taglist destructor. - * \param widget The widget to destroy. - */ -static void -taglist_destructor(widget_t *widget) -{ - taglist_data_t *d = widget->data; - - taglist_drawn_area_list_wipe(&d->drawn_area); - p_delete(&d); -} - -/** Taglist detach function. - * \param widget The widget which is detaching. - * \param object The object we are leaving. - */ -static void -taglist_detach(widget_t *widget, wibox_t *object) -{ - taglist_data_t *d = widget->data; - taglist_drawn_area_t *tda; - - if((tda = taglist_drawn_area_getbyobj(d->drawn_area, object))) - { - taglist_drawn_area_list_detach(&d->drawn_area, tda); - taglist_drawn_area_delete(&tda); - } -} - -/** Create a brand new taglist widget. - * \param align Widget alignment. - * \return A taglist widget. - */ -widget_t * -taglist_new(alignment_t align) -{ - widget_t *w; - taglist_data_t *d; - - w = p_new(widget_t, 1); - widget_common_new(w); - w->index = luaA_taglist_index; - w->newindex = luaA_taglist_newindex; - w->align = align; - w->draw = taglist_draw; - w->button = taglist_button; - w->destructor = taglist_destructor; - w->detach = taglist_detach; - - w->data = d = p_new(taglist_data_t, 1); - d->label = LUA_REFNIL; - - /* Set cache property */ - w->cache_flags = WIDGET_CACHE_TAGS | WIDGET_CACHE_CLIENTS; - - return w; -} - -// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/widgets/tasklist.c b/widgets/tasklist.c deleted file mode 100644 index 9d14d375c..000000000 --- a/widgets/tasklist.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * tasklist.c - task list widget - * - * 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 "client.h" -#include "widget.h" -#include "tag.h" -#include "wibox.h" -#include "common/markup.h" -#include "common/tokenize.h" - -extern awesome_t globalconf; - -/** Link a client and a label */ -typedef struct -{ - /** A client */ - client_t *client; - /** The client label */ - char *label; - /** The client label len */ - size_t label_len; -} client_label_t; - -/** Delete a client label. - * \param l The client label. - */ -static void -client_label_wipe(client_label_t *l) -{ - p_delete(&l->label); -} - -DO_ARRAY(client_label_t, client_label, client_label_wipe) - -typedef struct tasklist_object_data_t tasklist_object_data_t; -/** Link an object with a client label array and other infos */ -struct tasklist_object_data_t -{ - /** The object */ - wibox_t *object; - /** The box width for each client */ - int box_width; - /** The client label array for the object */ - client_label_array_t client_labels; - /** This is a list */ - tasklist_object_data_t *prev, *next; -}; - -static void -tasklist_object_data_delete(tasklist_object_data_t **l) -{ - client_label_array_wipe(&(*l)->client_labels); - p_delete(l); -} - -DO_SLIST(tasklist_object_data_t, tasklist_object_data, tasklist_object_data_delete) - -/** The tasklist private data structure. */ -typedef struct -{ - bool show_icons; - luaA_ref label; - tasklist_object_data_t *objects_data; - bool invert; -} tasklist_data_t; - -/** Get an object data by its object. - * \param od The object data list. - * \param p The object. - * \return A object data or NULL if not found. - */ -static tasklist_object_data_t * -tasklist_object_data_getbyobj(tasklist_object_data_t *od, wibox_t *p) -{ - tasklist_object_data_t *o; - - for(o = od; o; o = o->next) - if(o->object == p) - return o; - - return NULL; -} - -/** Draw an item in a tasklist widget. - * \param ctx The draw context, must be the same used to draw the tasklist - * \param w Tasklist widget node - * \param odata Tasklist object data - * \param width_mod Width modification of the item - * \param pos Position to draw the item at - * \param i Item to draw - */ -static void -tasklist_draw_item(draw_context_t *ctx, - widget_node_t *w, - tasklist_object_data_t *odata, - int width_mod, int pos, int i) { - draw_parser_data_t pdata, *parser_data; - image_t *image; - area_t area; - tasklist_data_t *d = w->widget->data; - int icon_width = 0; - - if(d->show_icons) - { - draw_parser_data_init(&pdata); - - /* Actually look for the proper background color, since - * otherwise the background wibox color is used instead */ - if(draw_text_markup_expand(&pdata, - odata->client_labels.tab[i].label, - odata->client_labels.tab[i].label_len)) - { - parser_data = &pdata; - if(pdata.has_bg_color) - { - /* draw a background for icons */ - area.x = w->area.x + pos; - area.y = w->area.y; - area.height = ctx->height; - area.width = odata->box_width; - draw_rectangle(ctx, area, 1.0, true, &pdata.bg_color); - } - } - else - parser_data = NULL; - - if(odata->client_labels.tab[i].client->icon) - { - image = odata->client_labels.tab[i].client->icon; - icon_width = ((double) ctx->height / (double) image->height) * image->width; - draw_image(ctx, w->area.x + odata->box_width * i, - w->area.y, ctx->height, image); - } - } - else - parser_data = NULL; - - area.x = w->area.x + icon_width + pos; - area.y = w->area.y; - area.width = odata->box_width - icon_width + width_mod; - area.height = ctx->height; - - draw_text(ctx, globalconf.font, area, - odata->client_labels.tab[i].label, - odata->client_labels.tab[i].label_len, - parser_data); - draw_parser_data_wipe(parser_data); -} - -/** Draw a tasklist widget. - * \param ctx The draw context. - * \param screen The screen number. - * \param w The widget node we are called from. - * \param offset The offset to draw at. - * \param used The already used width. - * \param p A pointer to the object we're drawing onto. - * \return The widget width. - */ -static int -tasklist_draw(draw_context_t *ctx, int screen, - widget_node_t *w, - int offset, int used, wibox_t *p) -{ - client_t *c; - tasklist_data_t *d = w->widget->data; - int i = 0, box_width_rest = 0, pos = 0; - tasklist_object_data_t *odata; - - if(used >= ctx->width) - return (w->area.width = 0); - - if(!(odata = tasklist_object_data_getbyobj(d->objects_data, p))) - { - odata = p_new(tasklist_object_data_t, 1); - odata->object = p; - tasklist_object_data_list_push(&d->objects_data, odata); - } - - client_label_array_wipe(&odata->client_labels); - client_label_array_init(&odata->client_labels); - - for(c = globalconf.clients; c; c = c->next) - if(!c->skiptb - && !c->ishidden - && c->type != WINDOW_TYPE_SPLASH - && c->type != WINDOW_TYPE_DOCK - && c->type != WINDOW_TYPE_DESKTOP) - { - /* push client */ - luaA_client_userdata_new(globalconf.L, c); - /* push screen we're at */ - lua_pushnumber(globalconf.L, screen + 1); - /* call label function with client as argument and wait for one - * result */ - if(luaA_dofunction(globalconf.L, d->label, 2, 1)) - { - /* If we got a string as returned value, we got something to write: - * a label. So we store it in a client_label_t structure, pushed - * into the client_label_array_t which is owned by the object. */ - if(lua_isstring(globalconf.L, -1)) - { - client_label_t cl; - cl.client = c; - cl.label = a_strdup(lua_tolstring(globalconf.L, -1, &cl.label_len)); - client_label_array_append(&odata->client_labels, cl); - } - - lua_pop(globalconf.L, 1); - } - } - - if(!odata->client_labels.len) - return (w->area.width = 0); - - odata->box_width = (ctx->width - used) / odata->client_labels.len; - /* compute how many pixel we left empty */ - box_width_rest = (ctx->width - used) % odata->client_labels.len; - - w->area.x = widget_calculate_offset(ctx->width, - 0, offset, w->widget->align); - - w->area.y = 0; - - if(d->invert) - for(i = odata->client_labels.len - 1; i >= 0; i--) - { - /* if we're on last elem, it has the last pixels left. */ - if (i == 0) - tasklist_draw_item(ctx, w, odata, 0, pos, i); - else - tasklist_draw_item(ctx, w, odata, box_width_rest, pos, i); - pos += odata->box_width; - } - else - for(i = 0; i < odata->client_labels.len; i++) - { - /* if we're on last elem, it has the last pixels left. */ - if(i != odata->client_labels.len - 1) - tasklist_draw_item(ctx, w, odata, 0, pos , i); - else - tasklist_draw_item(ctx, w, odata, box_width_rest, pos, i); - pos += odata->box_width; - } - - w->area.width = ctx->width - used; - w->area.height = ctx->height; - - return w->area.width; -} - -/** Handle button click on tasklist. - * \param w The widget node. - * \param ev The button press event. - * \param screen The screen where the click was. - * \param object The object we're onto. - */ -static void -tasklist_button(widget_node_t *w, - xcb_button_press_event_t *ev, - int screen, - wibox_t *object) -{ - tasklist_data_t *d = w->widget->data; - int ci = 0; - tasklist_object_data_t *odata; - button_array_t *barr = &w->widget->buttons; - - odata = tasklist_object_data_getbyobj(d->objects_data, object); - - if(!odata || !odata->client_labels.len) - return; - - if (!d->invert) - ci = (ev->event_x - w->area.x) / odata->box_width; - else - ci = odata->client_labels.len - 1 - (ev->event_x - w->area.x) / odata->box_width; - - for(int i = 0; i < barr->len; i++) - if(ev->detail == barr->tab[i]->button - && XUTIL_MASK_CLEAN(ev->state) == barr->tab[i]->mod) - { - luaA_wibox_userdata_new(globalconf.L, object); - luaA_client_userdata_new(globalconf.L, odata->client_labels.tab[ci].client); - luaA_dofunction(globalconf.L, - ev->response_type == XCB_BUTTON_PRESS ? - barr->tab[i]->press : barr->tab[i]->release, - 2, 0); - } -} - -/** Tasklist widget. - * \param L The Lua VM state. - * \param token The key token. - * \return The number of elements pushed on stack. - * \luastack - * \lfield show_icons Show icons near client title. - * \lfield label Function used to get the string to display as the window title. - * It gets the client and a screen number as argument, and must return a string. - */ -static int -luaA_tasklist_index(lua_State *L, awesome_token_t token) -{ - widget_t **widget = luaA_checkudata(L, 1, "widget"); - tasklist_data_t *d = (*widget)->data; - - switch(token) - { - case A_TK_SHOW_ICONS: - lua_pushboolean(L, d->show_icons); - return 1; - case A_TK_LABEL: - lua_rawgeti(L, LUA_REGISTRYINDEX, d->label); - return 1; - case A_TK_INVERT: - lua_pushboolean(L, d->invert); - return 1; - default: - return 0; - } -} - -/** Newindex function for tasklist widget. - * \param L The Lua VM state. - * \param token The key token. - * \return The number of elements pushed on stack. - */ -static int -luaA_tasklist_newindex(lua_State *L, awesome_token_t token) -{ - widget_t **widget = luaA_checkudata(L, 1, "widget"); - tasklist_data_t *d = (*widget)->data; - - switch(token) - { - case A_TK_SHOW_ICONS: - d->show_icons = luaA_checkboolean(L, 3); - break; - case A_TK_LABEL: - luaA_registerfct(L, 3, &d->label); - break; - case A_TK_INVERT: - d->invert = luaA_checkboolean(L, 3); - break; - default: - return 0; - } - - widget_invalidate_bywidget(*widget); - - return 0; -} - -/** Destructor for the tasklist widget. - * \param widget The widget to destroy. - */ -static void -tasklist_destructor(widget_t *widget) -{ - tasklist_data_t *d = widget->data; - tasklist_object_data_list_wipe(&d->objects_data); - p_delete(&d); -} - -/** Tasklist detach function. - * \param widget The widget which is detaching. - * \param object The object we are leaving. - */ -static void -tasklist_detach(widget_t *widget, wibox_t *object) -{ - tasklist_data_t *d = widget->data; - tasklist_object_data_t *od; - - if((od = tasklist_object_data_getbyobj(d->objects_data, object))) - { - tasklist_object_data_list_detach(&d->objects_data, od); - tasklist_object_data_delete(&od); - } -} - -/** Create a new widget tasklist. - * \param align The widget alignment, which is flex anyway. - * \return A brand new tasklist widget. - */ -widget_t * -tasklist_new(alignment_t align __attribute__ ((unused))) -{ - widget_t *w; - tasklist_data_t *d; - - w = p_new(widget_t, 1); - widget_common_new(w); - w->draw = tasklist_draw; - w->button = tasklist_button; - w->align = AlignFlex; - w->index = luaA_tasklist_index; - w->newindex = luaA_tasklist_newindex; - w->data = d = p_new(tasklist_data_t, 1); - w->destructor = tasklist_destructor; - w->detach = tasklist_detach; - - d->show_icons = true; - d->label = LUA_REFNIL; - d->invert = false; - - /* Set cache property */ - w->cache_flags = WIDGET_CACHE_CLIENTS | WIDGET_CACHE_TAGS; - - return w; -} - -// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80