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 <julien@danjou.info>
This commit is contained in:
Julien Danjou 2008-10-19 19:14:55 +02:00
parent 0e3ff0bc8f
commit 862fe193ee
21 changed files with 599 additions and 957 deletions

View File

@ -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)

View File

@ -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
-- }}}

View File

@ -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))

View File

@ -37,6 +37,7 @@ invert
label
layout
left
len
line
Lock
machine
@ -87,6 +88,7 @@ type
urgent
visible
vertical
widgets
width
workarea
yes

1
ewmh.c
View File

@ -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);
}
}

View File

@ -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

View File

@ -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 = "<margin left=\"2\" right=\"2\"/>"
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 .. "<bg color='"..bg_focus.."'/><span color='"..util.color_strip_alpha(fg_focus).."'>"..name.."</span>"
else
text = text .. name
end
elseif c.urgent and bg_urgent and fg_urgent then
local bg = bg_urgent
text = text .. "<bg color='"..bg_urgent.."'/><span color='"..util.color_strip_alpha(fg_urgent).."'>"..name.."</span>"
else
text = text .. name
end
return text
return text, bg
end
--- Return labels for a tasklist widget with clients from all tags and screen.

View File

@ -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",

View File

@ -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)

260
luaa.c
View File

@ -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;
}

16
luaa.h
View File

@ -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 { \

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

22
tag.c
View File

@ -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;

163
wibox.c
View File

@ -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 },

11
wibox.h
View File

@ -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)

View File

@ -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.

View File

@ -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;

View File

@ -1,306 +0,0 @@
/*
* taglist.c - tag list widget
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
*
* 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

View File

@ -1,430 +0,0 @@
/*
* tasklist.c - task list widget
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
*
* 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