1---------------------------------------------------------------------------
2--- Tasklist widget module for awful.
3--
4-- <a name="status_icons"></a>
5-- **Status icons:**
6--
7-- By default, the tasklist prepends some symbols in front of the client name.
8-- This is used to notify that the client has some specific properties that are
9-- currently enabled. This can be disabled using
10-- `beautiful.tasklist_plain_task_name`=true in the theme.
11--
12-- <table class='widget_list' border=1>
13-- <tr style='font-weight: bold;'>
14--  <th align='center'>Icon</th>
15--  <th align='center'>Client property</th>
16-- </tr>
17-- <tr><td>▪</td><td><a href="./client.html#client.sticky">sticky</a></td></tr>
18-- <tr><td>⌃</td><td><a href="./client.html#client.ontop">ontop</a></td></tr>
19-- <tr><td>▴</td><td><a href="./client.html#client.above">above</a></td></tr>
20-- <tr><td>▾</td><td><a href="./client.html#client.below">below</a></td></tr>
21-- <tr><td>✈</td><td><a href="./client.html#client.floating">floating</a></td></tr>
22-- <tr><td>+</td><td><a href="./client.html#client.maximized">maximized</a></td></tr>
23-- <tr><td>⬌</td><td><a href="./client.html#client.maximized_horizontal">maximized_horizontal</a></td></tr>
24-- <tr><td>⬍</td><td><a href="./client.html#client.maximized_vertical">maximized_vertical</a></td></tr>
25-- </table>
26--
27-- **Customizing the tasklist:**
28--
29-- The `tasklist` created by `rc.lua` use the default values for almost
30-- everything. However, it is possible to override each aspects to create a
31-- very different widget. Here's an example that create a tasklist similar to
32-- the default one, but with an explicit layout and some spacing widgets:
33--
34--@DOC_wibox_awidget_tasklist_rounded_EXAMPLE@
35--
36-- As demonstrated in the example above, there are a few "shortcuts" to avoid
37-- re-inventing the wheel. By setting the predefined roles as widget `id`s,
38-- `awful.widget.common` will do most of the work to update the values
39-- automatically. All of them are optional. The supported roles are:
40--
41-- * `icon_role`: A `wibox.widget.imagebox`
42-- * `text_role`: A `wibox.widget.textbox`
43-- * `background_role`: A `wibox.container.background`
44-- * `text_margin_role`: A `wibox.container.margin`
45-- * `icon_margin_role`: A `wibox.container.margin`
46--
47-- `awful.widget.common` also has 2 callbacks to give more control over the widget:
48--
49-- * `create_callback`: Called once after the widget instance is created
50-- * `update_callback`: Called everytime the data is refreshed
51--
52-- Both callback have the same parameters:
53--
54-- * `self`: The widget instance (*widget*).
55-- * `c`: The client (*client*)
56-- * `index`: The widget position in the list (*number*)
57-- * `clients`: The list of client, in order (*table*)
58--
59-- It is also possible to omit some roles and create an icon only tasklist.
60-- Notice that this example use the `awful.widget.clienticon` widget instead
61-- of an `imagebox`. This allows higher resoluton icons to be loaded. This
62-- example reproduces the Windows 10 tasklist look and feel:
63--
64--@DOC_wibox_awidget_tasklist_windows10_EXAMPLE@
65--
66-- The tasklist can also be created in an `awful.popup` in case there is no
67-- permanent `awful.wibar`:
68--
69--@DOC_awful_popup_alttab_EXAMPLE@
70--
71-- @author Julien Danjou &lt;julien@danjou.info&gt;
72-- @copyright 2008-2009 Julien Danjou
73-- @classmod awful.widget.tasklist
74---------------------------------------------------------------------------
75
76-- Grab environment we need
77local capi = { screen = screen,
78               client = client }
79local ipairs = ipairs
80local setmetatable = setmetatable
81local table = table
82local common = require("awful.widget.common")
83local beautiful = require("beautiful")
84local tag = require("awful.tag")
85local flex = require("wibox.layout.flex")
86local timer = require("gears.timer")
87local gcolor = require("gears.color")
88local gstring = require("gears.string")
89local gdebug = require("gears.debug")
90local base = require("wibox.widget.base")
91
92local function get_screen(s)
93    return s and screen[s]
94end
95
96local tasklist = { mt = {} }
97
98local instances
99
100--- The default foreground (text) color.
101-- @beautiful beautiful.tasklist_fg_normal
102-- @tparam[opt=nil] string|pattern fg_normal
103-- @see gears.color
104
105--- The default background color.
106-- @beautiful beautiful.tasklist_bg_normal
107-- @tparam[opt=nil] string|pattern bg_normal
108-- @see gears.color
109
110--- The focused client foreground (text) color.
111-- @beautiful beautiful.tasklist_fg_focus
112-- @tparam[opt=nil] string|pattern fg_focus
113-- @see gears.color
114
115--- The focused client background color.
116-- @beautiful beautiful.tasklist_bg_focus
117-- @tparam[opt=nil] string|pattern bg_focus
118-- @see gears.color
119
120--- The urgent clients foreground (text) color.
121-- @beautiful beautiful.tasklist_fg_urgent
122-- @tparam[opt=nil] string|pattern fg_urgent
123-- @see gears.color
124
125--- The urgent clients background color.
126-- @beautiful beautiful.tasklist_bg_urgent
127-- @tparam[opt=nil] string|pattern bg_urgent
128-- @see gears.color
129
130--- The minimized clients foreground (text) color.
131-- @beautiful beautiful.tasklist_fg_minimize
132-- @tparam[opt=nil] string|pattern fg_minimize
133-- @see gears.color
134
135--- The minimized clients background color.
136-- @beautiful beautiful.tasklist_bg_minimize
137-- @tparam[opt=nil] string|pattern bg_minimize
138-- @see gears.color
139
140--- The elements default background image.
141-- @beautiful beautiful.tasklist_bg_image_normal
142-- @tparam[opt=nil] string bg_image_normal
143
144--- The focused client background image.
145-- @beautiful beautiful.tasklist_bg_image_focus
146-- @tparam[opt=nil] string bg_image_focus
147
148--- The urgent clients background image.
149-- @beautiful beautiful.tasklist_bg_image_urgent
150-- @tparam[opt=nil] string bg_image_urgent
151
152--- The minimized clients background image.
153-- @beautiful beautiful.tasklist_bg_image_minimize
154-- @tparam[opt=nil] string bg_image_minimize
155
156--- Disable the tasklist client icons.
157-- @beautiful beautiful.tasklist_disable_icon
158-- @tparam[opt=false] boolean tasklist_disable_icon
159
160--- Disable the tasklist client titles.
161-- @beautiful beautiful.tasklist_disable_task_name
162-- @tparam[opt=false] boolean tasklist_disable_task_name
163
164--- Disable the extra tasklist client property notification icons.
165--
166-- See the <a href="status_icons">Status icons</a> section for more details.
167--
168-- @beautiful beautiful.tasklist_plain_task_name
169-- @tparam[opt=false] boolean tasklist_plain_task_name
170
171--- The tasklist font.
172-- @beautiful beautiful.tasklist_font
173-- @tparam[opt=nil] string font
174
175--- The focused client alignment.
176-- @beautiful beautiful.tasklist_align
177-- @tparam[opt=left] string align *left*, *right* or *center*
178
179--- The focused client title alignment.
180-- @beautiful beautiful.tasklist_font_focus
181-- @tparam[opt=nil] string font_focus
182
183--- The minimized clients font.
184-- @beautiful beautiful.tasklist_font_minimized
185-- @tparam[opt=nil] string font_minimized
186
187--- The urgent clients font.
188-- @beautiful beautiful.tasklist_font_urgent
189-- @tparam[opt=nil] string font_urgent
190
191--- The space between the tasklist elements.
192-- @beautiful beautiful.tasklist_spacing
193-- @tparam[opt=0] number spacing The spacing between tasks.
194
195--- The default tasklist elements shape.
196-- @beautiful beautiful.tasklist_shape
197-- @tparam[opt=nil] gears.shape shape
198
199--- The default tasklist elements border width.
200-- @beautiful beautiful.tasklist_shape_border_width
201-- @tparam[opt=0] number shape_border_width
202
203--- The default tasklist elements border color.
204-- @beautiful beautiful.tasklist_shape_border_color
205-- @tparam[opt=nil] string|color shape_border_color
206-- @see gears.color
207
208--- The focused client shape.
209-- @beautiful beautiful.tasklist_shape_focus
210-- @tparam[opt=nil] gears.shape shape_focus
211
212--- The focused client border width.
213-- @beautiful beautiful.tasklist_shape_border_width_focus
214-- @tparam[opt=0] number shape_border_width_focus
215
216--- The focused client border color.
217-- @beautiful beautiful.tasklist_shape_border_color_focus
218-- @tparam[opt=nil] string|color shape_border_color_focus
219-- @see gears.color
220
221--- The minimized clients shape.
222-- @beautiful beautiful.tasklist_shape_minimized
223-- @tparam[opt=nil] gears.shape shape_minimized
224
225--- The minimized clients border width.
226-- @beautiful beautiful.tasklist_shape_border_width_minimized
227-- @tparam[opt=0] number shape_border_width_minimized
228
229--- The minimized clients border color.
230-- @beautiful beautiful.tasklist_shape_border_color_minimized
231-- @tparam[opt=nil] string|color shape_border_color_minimized
232-- @see gears.color
233
234--- The urgent clients shape.
235-- @beautiful beautiful.tasklist_shape_urgent
236-- @tparam[opt=nil] gears.shape shape_urgent
237
238--- The urgent clients border width.
239-- @beautiful beautiful.tasklist_shape_border_width_urgent
240-- @tparam[opt=0] number shape_border_width_urgent
241
242--- The urgent clients border color.
243-- @beautiful beautiful.tasklist_shape_border_color_urgent
244-- @tparam[opt=nil] string|color shape_border_color_urgent
245-- @see gears.color
246
247-- Public structures
248tasklist.filter, tasklist.source = {}, {}
249
250local function tasklist_label(c, args, tb)
251    if not args then args = {} end
252    local theme = beautiful.get()
253    local align = args.align or theme.tasklist_align or "left"
254    local fg_normal = gcolor.ensure_pango_color(args.fg_normal or theme.tasklist_fg_normal or theme.fg_normal, "white")
255    local bg_normal = args.bg_normal or theme.tasklist_bg_normal or theme.bg_normal or "#000000"
256    local fg_focus = gcolor.ensure_pango_color(args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus, fg_normal)
257    local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus or bg_normal
258    local fg_urgent = gcolor.ensure_pango_color(args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent,
259                                                fg_normal)
260    local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent or bg_normal
261    local fg_minimize = gcolor.ensure_pango_color(args.fg_minimize or theme.tasklist_fg_minimize or theme.fg_minimize,
262                                                  fg_normal)
263    local bg_minimize = args.bg_minimize or theme.tasklist_bg_minimize or theme.bg_minimize or bg_normal
264    -- FIXME v5, remove the fallback theme.bg_image_* variables, see GH#1403
265    local bg_image_normal = args.bg_image_normal or theme.tasklist_bg_image_normal or theme.bg_image_normal
266    local bg_image_focus = args.bg_image_focus or theme.tasklist_bg_image_focus or theme.bg_image_focus
267    local bg_image_urgent = args.bg_image_urgent or theme.tasklist_bg_image_urgent or theme.bg_image_urgent
268    local bg_image_minimize = args.bg_image_minimize or theme.tasklist_bg_image_minimize or theme.bg_image_minimize
269    local tasklist_disable_icon = args.tasklist_disable_icon or theme.tasklist_disable_icon or false
270    local disable_task_name = args.disable_task_name or theme.tasklist_disable_task_name or false
271    local font = args.font or theme.tasklist_font or theme.font or ""
272    local font_focus = args.font_focus or theme.tasklist_font_focus or theme.font_focus or font or ""
273    local font_minimized = args.font_minimized or theme.tasklist_font_minimized or theme.font_minimized or font or ""
274    local font_urgent = args.font_urgent or theme.tasklist_font_urgent or theme.font_urgent or font or ""
275    local text = ""
276    local name = ""
277    local bg
278    local bg_image
279    local shape              = args.shape or theme.tasklist_shape
280    local shape_border_width = args.shape_border_width or theme.tasklist_shape_border_width
281    local shape_border_color = args.shape_border_color or theme.tasklist_shape_border_color
282
283    -- symbol to use to indicate certain client properties
284    local sticky = args.sticky or theme.tasklist_sticky or "▪"
285    local ontop = args.ontop or theme.tasklist_ontop or '⌃'
286    local above = args.above or theme.tasklist_above or '▴'
287    local below = args.below or theme.tasklist_below or '▾'
288    local floating = args.floating or theme.tasklist_floating or '✈'
289    local maximized = args.maximized or theme.tasklist_maximized or '<b>+</b>'
290    local maximized_horizontal = args.maximized_horizontal or theme.tasklist_maximized_horizontal or '⬌'
291    local maximized_vertical = args.maximized_vertical or theme.tasklist_maximized_vertical or '⬍'
292
293    if tb then
294        tb:set_align(align)
295    end
296
297    if not theme.tasklist_plain_task_name then
298        if c.sticky then name = name .. sticky end
299
300        if c.ontop then name = name .. ontop
301        elseif c.above then name = name .. above
302        elseif c.below then name = name .. below end
303
304        if c.maximized then
305            name = name .. maximized
306        else
307            if c.maximized_horizontal then name = name .. maximized_horizontal end
308            if c.maximized_vertical then name = name .. maximized_vertical end
309            if c.floating then name = name .. floating end
310        end
311    end
312
313    if not disable_task_name then
314        if c.minimized then
315            name = name .. (gstring.xml_escape(c.icon_name) or gstring.xml_escape(c.name) or
316                            gstring.xml_escape("<untitled>"))
317        else
318            name = name .. (gstring.xml_escape(c.name) or gstring.xml_escape("<untitled>"))
319        end
320    end
321
322    local focused = capi.client.focus == c
323    -- Handle transient_for: the first parent that does not skip the taskbar
324    -- is considered to be focused, if the real client has skip_taskbar.
325    if not focused and capi.client.focus and capi.client.focus.skip_taskbar
326        and capi.client.focus:get_transient_for_matching(function(cl)
327                                                             return not cl.skip_taskbar
328                                                         end) == c then
329        focused = true
330    end
331
332    if focused then
333        bg = bg_focus
334        text = text .. "<span color='"..fg_focus.."'>"..name.."</span>"
335        bg_image = bg_image_focus
336        font = font_focus
337
338        if args.shape_focus or theme.tasklist_shape_focus then
339            shape = args.shape_focus or theme.tasklist_shape_focus
340        end
341
342        if args.shape_border_width_focus or theme.tasklist_shape_border_width_focus then
343            shape_border_width = args.shape_border_width_focus or theme.tasklist_shape_border_width_focus
344        end
345
346        if args.shape_border_color_focus or theme.tasklist_shape_border_color_focus then
347            shape_border_color = args.shape_border_color_focus or theme.tasklist_shape_border_color_focus
348        end
349    elseif c.urgent then
350        bg = bg_urgent
351        text = text .. "<span color='"..fg_urgent.."'>"..name.."</span>"
352        bg_image = bg_image_urgent
353        font = font_urgent
354
355        if args.shape_urgent or theme.tasklist_shape_urgent then
356            shape = args.shape_urgent or theme.tasklist_shape_urgent
357        end
358
359        if args.shape_border_width_urgent or theme.tasklist_shape_border_width_urgent then
360            shape_border_width = args.shape_border_width_urgent or theme.tasklist_shape_border_width_urgent
361        end
362
363        if args.shape_border_color_urgent or theme.tasklist_shape_border_color_urgent then
364            shape_border_color = args.shape_border_color_urgent or theme.tasklist_shape_border_color_urgent
365        end
366    elseif c.minimized then
367        bg = bg_minimize
368        text = text .. "<span color='"..fg_minimize.."'>"..name.."</span>"
369        bg_image = bg_image_minimize
370        font = font_minimized
371
372        if args.shape_minimized or theme.tasklist_shape_minimized then
373            shape = args.shape_minimized or theme.tasklist_shape_minimized
374        end
375
376        if args.shape_border_width_minimized or theme.tasklist_shape_border_width_minimized then
377            shape_border_width = args.shape_border_width_minimized or theme.tasklist_shape_border_width_minimized
378        end
379
380        if args.shape_border_color_minimized or theme.tasklist_shape_border_color_minimized then
381            shape_border_color = args.shape_border_color_minimized or theme.tasklist_shape_border_color_minimized
382        end
383    else
384        bg = bg_normal
385        text = text .. "<span color='"..fg_normal.."'>"..name.."</span>"
386        bg_image = bg_image_normal
387    end
388
389    if tb then
390        tb:set_font(font)
391    end
392
393    local other_args = {
394        shape              = shape,
395        shape_border_width = shape_border_width,
396        shape_border_color = shape_border_color,
397    }
398
399    return text, bg, bg_image, not tasklist_disable_icon and c.icon or nil, other_args
400end
401
402local function tasklist_update(s, w, buttons, filter, data, style, update_function, args)
403    local clients = {}
404
405    local source = args and args.source or tasklist.source.all_clients or nil
406    local list   = source and source(s, args) or capi.client.get()
407
408    for _, c in ipairs(list) do
409        if not (c.skip_taskbar or c.hidden
410            or c.type == "splash" or c.type == "dock" or c.type == "desktop")
411            and filter(c, s) then
412            table.insert(clients, c)
413        end
414    end
415
416    local function label(c, tb) return tasklist_label(c, style, tb) end
417
418    update_function(w, buttons, label, data, clients, args)
419end
420
421--- Create a new tasklist widget.
422-- The last two arguments (update_function
423-- and layout) serve to customize the layout of the tasklist (eg. to
424-- make it vertical). For that, you will need to copy the
425-- awful.widget.common.list_update function, make your changes to it
426-- and pass it as update_function here. Also change the layout if the
427-- default is not what you want.
428--
429-- @tparam table args
430-- @tparam screen args.screen The screen to draw tasklist for.
431-- @tparam function args.filter Filter function to define what clients will be listed.
432-- @tparam table args.buttons A table with buttons binding to set.
433-- @tparam[opt] function args.update_function Function to create a tag widget on each
434--   update. See `awful.widget.common.list_update`.
435-- @tparam[opt] table args.layout Container widget for tag widgets. Default
436--   is `wibox.layout.flex.horizontal`.
437-- @tparam[opt=awful.tasklist.source.all_clients] function args.source The
438--  function used to generate the list of client.
439-- @tparam[opt] table args.widget_template A custom widget to be used for each client
440-- @tparam[opt={}] table args.style The style overrides default theme.
441-- @tparam[opt=nil] string|pattern args.style.fg_normal
442-- @tparam[opt=nil] string|pattern args.style.bg_normal
443-- @tparam[opt=nil] string|pattern args.style.fg_focus
444-- @tparam[opt=nil] string|pattern args.style.bg_focus
445-- @tparam[opt=nil] string|pattern args.style.fg_urgent
446-- @tparam[opt=nil] string|pattern args.style.bg_urgent
447-- @tparam[opt=nil] string|pattern args.style.fg_minimize
448-- @tparam[opt=nil] string|pattern args.style.bg_minimize
449-- @tparam[opt=nil] string args.style.bg_image_normal
450-- @tparam[opt=nil] string args.style.bg_image_focus
451-- @tparam[opt=nil] string args.style.bg_image_urgent
452-- @tparam[opt=nil] string args.style.bg_image_minimize
453-- @tparam[opt=nil] boolean args.style.tasklist_disable_icon
454-- @tparam[opt=false] boolean args.style.disable_task_name
455-- @tparam[opt=nil] string args.style.font
456-- @tparam[opt=left] string args.style.align *left*, *right* or *center*
457-- @tparam[opt=nil] string args.style.font_focus
458-- @tparam[opt=nil] string args.style.font_minimized
459-- @tparam[opt=nil] string args.style.font_urgent
460-- @tparam[opt=nil] number args.style.spacing The spacing between tags.
461-- @tparam[opt=nil] gears.shape args.style.shape
462-- @tparam[opt=nil] number args.style.shape_border_width
463-- @tparam[opt=nil] string|color args.style.shape_border_color
464-- @tparam[opt=nil] gears.shape args.style.shape_focus
465-- @tparam[opt=nil] number args.style.shape_border_width_focus
466-- @tparam[opt=nil] string|color args.style.shape_border_color_focus
467-- @tparam[opt=nil] gears.shape args.style.shape_minimized
468-- @tparam[opt=nil] number args.style.shape_border_width_minimized
469-- @tparam[opt=nil] string|color args.style.shape_border_color_minimized
470-- @tparam[opt=nil] gears.shape args.style.shape_urgent
471-- @tparam[opt=nil] number args.style.shape_border_width_urgent
472-- @tparam[opt=nil] string|color args.style.shape_border_color_urgent
473-- @param filter **DEPRECATED** use args.filter
474-- @param buttons **DEPRECATED** use args.buttons
475-- @param style **DEPRECATED** use args.style
476-- @param update_function **DEPRECATED** use args.update_function
477-- @param base_widget **DEPRECATED** use args.base_widget
478-- @function awful.tasklist
479function tasklist.new(args, filter, buttons, style, update_function, base_widget)
480    local screen = nil
481
482    local argstype = type(args)
483
484    -- Detect the old function signature
485    if argstype == "number" or argstype == "screen" or
486      (argstype == "table" and args.index and args == capi.screen[args.index]) then
487        gdebug.deprecate("The `screen` paramater is deprecated, use `args.screen`.",
488            {deprecated_in=5})
489
490        screen = get_screen(args)
491        args = {}
492    end
493
494    assert(type(args) == "table")
495
496    for k, v in pairs { filter          = filter,
497                        buttons         = buttons,
498                        style           = style,
499                        update_function = update_function,
500                        layout          = base_widget
501    } do
502        gdebug.deprecate("The `awful.widget.tasklist()` `"..k
503            .."` paramater is deprecated, use `args."..k.."`.",
504        {deprecated_in=5})
505        args[k] = v
506    end
507
508    screen = screen or get_screen(args.screen)
509    local uf = args.update_function or common.list_update
510    local w = base.make_widget_from_value(args.layout or flex.horizontal)
511
512    local data = setmetatable({}, { __mode = 'k' })
513
514    local spacing = args.style and args.style.spacing or args.layout and args.layout.spacing
515                    or beautiful.tasklist_spacing
516    if w.set_spacing and spacing then
517        w:set_spacing(spacing)
518    end
519
520    local queued_update = false
521
522    -- For the tests
523    function w._do_tasklist_update_now()
524        queued_update = false
525        if screen.valid then
526            tasklist_update(screen, w, args.buttons, args.filter, data, args.style, uf, args)
527        end
528    end
529
530    function w._do_tasklist_update()
531        -- Add a delayed callback for the first update.
532        if not queued_update then
533            timer.delayed_call(w._do_tasklist_update_now)
534            queued_update = true
535        end
536    end
537    function w._unmanage(c)
538        data[c] = nil
539    end
540    if instances == nil then
541        instances = setmetatable({}, { __mode = "k" })
542        local function us(s)
543            local i = instances[get_screen(s)]
544            if i then
545                for _, tlist in pairs(i) do
546                    tlist._do_tasklist_update()
547                end
548            end
549        end
550        local function u()
551            for s in pairs(instances) do
552                if s.valid then
553                    us(s)
554                end
555            end
556        end
557
558        tag.attached_connect_signal(nil, "property::selected", u)
559        tag.attached_connect_signal(nil, "property::activated", u)
560        capi.client.connect_signal("property::urgent", u)
561        capi.client.connect_signal("property::sticky", u)
562        capi.client.connect_signal("property::ontop", u)
563        capi.client.connect_signal("property::above", u)
564        capi.client.connect_signal("property::below", u)
565        capi.client.connect_signal("property::floating", u)
566        capi.client.connect_signal("property::maximized_horizontal", u)
567        capi.client.connect_signal("property::maximized_vertical", u)
568        capi.client.connect_signal("property::maximized", u)
569        capi.client.connect_signal("property::minimized", u)
570        capi.client.connect_signal("property::name", u)
571        capi.client.connect_signal("property::icon_name", u)
572        capi.client.connect_signal("property::icon", u)
573        capi.client.connect_signal("property::skip_taskbar", u)
574        capi.client.connect_signal("property::screen", function(c, old_screen)
575            us(c.screen)
576            us(old_screen)
577        end)
578        capi.client.connect_signal("property::hidden", u)
579        capi.client.connect_signal("tagged", u)
580        capi.client.connect_signal("untagged", u)
581        capi.client.connect_signal("unmanage", function(c)
582            u(c)
583            for _, i in pairs(instances) do
584                for _, tlist in pairs(i) do
585                    tlist._unmanage(c)
586                end
587            end
588        end)
589        capi.client.connect_signal("list", u)
590        capi.client.connect_signal("focus", u)
591        capi.client.connect_signal("unfocus", u)
592        capi.screen.connect_signal("removed", function(s)
593            instances[get_screen(s)] = nil
594        end)
595    end
596    w._do_tasklist_update()
597    local list = instances[screen]
598    if not list then
599        list = setmetatable({}, { __mode = "v" })
600        instances[screen] = list
601    end
602    table.insert(list, w)
603    return w
604end
605
606--- Filtering function to include all clients.
607-- @return true
608-- @filterfunction awful.tasklist.filter.allscreen
609function tasklist.filter.allscreen()
610    return true
611end
612
613--- Filtering function to include the clients from all tags on the screen.
614-- @param c The client.
615-- @param screen The screen we are drawing on.
616-- @return true if c is on screen, false otherwise
617-- @filterfunction awful.tasklist.filter.alltags
618function tasklist.filter.alltags(c, screen)
619    -- Only print client on the same screen as this widget
620    return get_screen(c.screen) == get_screen(screen)
621end
622
623--- Filtering function to include only the clients from currently selected tags.
624-- @param c The client.
625-- @param screen The screen we are drawing on.
626-- @return true if c is in a selected tag on screen, false otherwise
627-- @filterfunction awful.tasklist.filter.currenttags
628function tasklist.filter.currenttags(c, screen)
629    screen = get_screen(screen)
630    -- Only print client on the same screen as this widget
631    if get_screen(c.screen) ~= screen then return false end
632    -- Include sticky client too
633    if c.sticky then return true end
634    local tags = screen.tags
635    for _, t in ipairs(tags) do
636        if t.selected then
637            local ctags = c:tags()
638            for _, v in ipairs(ctags) do
639                if v == t then
640                    return true
641                end
642            end
643        end
644    end
645    return false
646end
647
648--- Filtering function to include only the minimized clients from currently selected tags.
649-- @param c The client.
650-- @param screen The screen we are drawing on.
651-- @return true if c is in a selected tag on screen and is minimized, false otherwise
652-- @filterfunction awful.tasklist.filter.minimizedcurrenttags
653function tasklist.filter.minimizedcurrenttags(c, screen)
654    screen = get_screen(screen)
655    -- Only print client on the same screen as this widget
656    if get_screen(c.screen) ~= screen then return false end
657    -- Check client is minimized
658    if not c.minimized then return false end
659    -- Include sticky client
660    if c.sticky then return true end
661    local tags = screen.tags
662    for _, t in ipairs(tags) do
663        -- Select only minimized clients
664        if t.selected then
665            local ctags = c:tags()
666            for _, v in ipairs(ctags) do
667                if v == t then
668                    return true
669                end
670            end
671        end
672    end
673    return false
674end
675
676--- Filtering function to include only the currently focused client.
677-- @param c The client.
678-- @param screen The screen we are drawing on.
679-- @return true if c is focused on screen, false otherwise
680-- @filterfunction awful.tasklist.filter.focused
681function tasklist.filter.focused(c, screen)
682    -- Only print client on the same screen as this widget
683    return get_screen(c.screen) == get_screen(screen) and capi.client.focus == c
684end
685
686--- Get all the clients in an undefined order.
687--
688-- This is the default source.
689--
690-- @sourcefunction awful.tasklist.source.all_clients
691function tasklist.source.all_clients()
692    return capi.client.get()
693end
694
695function tasklist.mt:__call(...)
696    return tasklist.new(...)
697end
698
699return setmetatable(tasklist, tasklist.mt)
700
701-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
702