2012-10-14 17:20:24 +02:00
|
|
|
---------------------------------------------------------------------------
|
2014-05-20 13:02:13 +02:00
|
|
|
--- Handling of drawables. A drawable is something that can be drawn to.
|
|
|
|
--
|
2012-10-14 17:20:24 +02:00
|
|
|
-- @author Uli Schlachter
|
|
|
|
-- @copyright 2012 Uli Schlachter
|
|
|
|
-- @release @AWESOME_VERSION@
|
2015-02-25 11:18:53 +01:00
|
|
|
-- @classmod wibox.drawable
|
2012-10-14 17:20:24 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
local drawable = {}
|
|
|
|
local capi = {
|
|
|
|
awesome = awesome,
|
2016-02-26 18:42:57 +01:00
|
|
|
root = root,
|
|
|
|
screen = screen
|
2012-10-14 17:20:24 +02:00
|
|
|
}
|
|
|
|
local beautiful = require("beautiful")
|
|
|
|
local cairo = require("lgi").cairo
|
|
|
|
local color = require("gears.color")
|
|
|
|
local object = require("gears.object")
|
|
|
|
local surface = require("gears.surface")
|
2015-01-11 10:58:49 +01:00
|
|
|
local timer = require("gears.timer")
|
2016-05-16 05:59:59 +02:00
|
|
|
local grect = require("gears.geometry").rectangle
|
2015-08-12 13:12:45 +02:00
|
|
|
local matrix = require("gears.matrix")
|
|
|
|
local hierarchy = require("wibox.hierarchy")
|
2016-02-07 14:13:43 +01:00
|
|
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
local drawables_draw = setmetatable({}, { __mode = 'k' })
|
|
|
|
local drawables_force_complete_repaint = setmetatable({}, { __mode = 'k' })
|
2012-11-03 19:43:31 +01:00
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
-- Get the widget context. This should always return the same table (if
|
|
|
|
-- possible), so that our draw and fit caches can work efficiently.
|
|
|
|
local function get_widget_context(self)
|
|
|
|
local geom = self.drawable:geometry()
|
2016-05-16 05:59:59 +02:00
|
|
|
|
|
|
|
local sgeos = {}
|
|
|
|
|
|
|
|
for s in capi.screen do
|
|
|
|
sgeos[s] = s.geometry
|
|
|
|
end
|
|
|
|
|
|
|
|
local s = grect.get_by_coord(sgeos, geom.x, geom.y) or capi.screen.primary
|
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
local context = self._widget_context
|
|
|
|
local dpi = beautiful.xresources.get_dpi(s)
|
|
|
|
if (not context) or context.screen ~= s or context.dpi ~= dpi then
|
|
|
|
context = {
|
|
|
|
screen = s,
|
|
|
|
dpi = dpi,
|
|
|
|
drawable = self,
|
|
|
|
}
|
|
|
|
for k, v in pairs(self._widget_context_skeleton) do
|
|
|
|
context[k] = v
|
|
|
|
end
|
|
|
|
self._widget_context = context
|
2016-09-23 10:03:12 +02:00
|
|
|
|
|
|
|
-- Give widgets a chance to react to the new context
|
|
|
|
self._need_complete_repaint = true
|
2015-08-08 13:13:47 +02:00
|
|
|
end
|
|
|
|
return context
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
local function do_redraw(self)
|
2016-09-23 10:18:49 +02:00
|
|
|
if not self.drawable.valid then return end
|
2016-09-07 06:41:10 +02:00
|
|
|
|
2016-01-17 17:38:52 +01:00
|
|
|
local surf = surface.load_silently(self.drawable.surface, false)
|
2013-02-20 14:19:31 +01:00
|
|
|
-- The surface can be nil if the drawable's parent was already finalized
|
|
|
|
if not surf then return end
|
|
|
|
local cr = cairo.Context(surf)
|
2012-10-14 17:20:24 +02:00
|
|
|
local geom = self.drawable:geometry();
|
|
|
|
local x, y, width, height = geom.x, geom.y, geom.width, geom.height
|
2016-02-08 09:37:09 +01:00
|
|
|
local context = get_widget_context(self)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2015-08-12 13:12:45 +02:00
|
|
|
-- Relayout
|
2015-09-15 12:49:20 +02:00
|
|
|
if self._need_relayout or self._need_complete_repaint then
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_relayout = false
|
2015-09-19 13:57:18 +02:00
|
|
|
if self._widget_hierarchy and self.widget then
|
2016-02-08 09:37:09 +01:00
|
|
|
self._widget_hierarchy:update(context,
|
2015-09-19 13:57:18 +02:00
|
|
|
self.widget, width, height, self._dirty_area)
|
|
|
|
else
|
|
|
|
self._need_complete_repaint = true
|
|
|
|
if self.widget then
|
|
|
|
self._widget_hierarchy_callback_arg = {}
|
2016-02-08 09:37:09 +01:00
|
|
|
self._widget_hierarchy = hierarchy.new(context, self.widget, width, height,
|
2015-09-19 13:57:18 +02:00
|
|
|
self._redraw_callback, self._layout_callback, self._widget_hierarchy_callback_arg)
|
|
|
|
else
|
|
|
|
self._widget_hierarchy = nil
|
|
|
|
end
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
|
2015-09-19 13:57:18 +02:00
|
|
|
if self._need_complete_repaint then
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_complete_repaint = false
|
|
|
|
self._dirty_area:union_rectangle(cairo.RectangleInt{
|
|
|
|
x = 0, y = 0, width = width, height = height
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Clip to the dirty area
|
|
|
|
if self._dirty_area:is_empty() then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
for i = 0, self._dirty_area:num_rectangles() - 1 do
|
|
|
|
local rect = self._dirty_area:get_rectangle(i)
|
|
|
|
cr:rectangle(rect.x, rect.y, rect.width, rect.height)
|
|
|
|
end
|
|
|
|
self._dirty_area = cairo.Region.create()
|
|
|
|
cr:clip()
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Draw the background
|
|
|
|
cr:save()
|
2014-02-23 00:02:57 +01:00
|
|
|
|
|
|
|
if not capi.awesome.composite_manager_running then
|
|
|
|
-- This is pseudo-transparency: We draw the wallpaper in the background
|
2016-03-27 11:29:28 +02:00
|
|
|
local wallpaper = surface.load_silently(capi.root.wallpaper(), false)
|
2014-02-23 00:02:57 +01:00
|
|
|
if wallpaper then
|
|
|
|
cr.operator = cairo.Operator.SOURCE
|
|
|
|
cr:set_source_surface(wallpaper, -x, -y)
|
|
|
|
cr:paint()
|
|
|
|
end
|
|
|
|
cr.operator = cairo.Operator.OVER
|
|
|
|
else
|
2014-02-23 12:19:26 +01:00
|
|
|
-- This is true transparency: We draw a translucent background
|
2012-10-14 17:20:24 +02:00
|
|
|
cr.operator = cairo.Operator.SOURCE
|
|
|
|
end
|
|
|
|
|
|
|
|
cr:set_source(self.background_color)
|
|
|
|
cr:paint()
|
2016-02-08 09:37:09 +01:00
|
|
|
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:restore()
|
|
|
|
|
2016-02-08 09:37:09 +01:00
|
|
|
-- Paint the background image
|
|
|
|
if self.background_image then
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:save()
|
2016-02-08 09:37:09 +01:00
|
|
|
if type(self.background_image) == "function" then
|
|
|
|
self.background_image(context, cr, width, height, unpack(self.background_image_args))
|
|
|
|
else
|
|
|
|
local pattern = cairo.Pattern.create_for_surface(self.background_image)
|
|
|
|
cr:set_source(pattern)
|
|
|
|
cr:paint()
|
|
|
|
end
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:restore()
|
2016-02-08 09:37:09 +01:00
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Draw the widget
|
2015-08-12 13:12:45 +02:00
|
|
|
if self._widget_hierarchy then
|
2012-10-14 17:20:24 +02:00
|
|
|
cr:set_source(self.foreground_color)
|
2016-02-08 09:37:09 +01:00
|
|
|
self._widget_hierarchy:draw(context, cr)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
self.drawable:refresh()
|
2014-04-01 16:20:45 +02:00
|
|
|
|
2015-09-06 13:23:05 +02:00
|
|
|
assert(cr.status == "SUCCESS", "Cairo context entered error state: " .. cr.status)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2016-02-07 14:13:43 +01:00
|
|
|
local function find_widgets(_drawable, result, _hierarchy, x, y)
|
|
|
|
local m = _hierarchy:get_matrix_from_device()
|
2015-08-12 13:12:45 +02:00
|
|
|
|
|
|
|
-- Is (x,y) inside of this hierarchy or any child (aka the draw extents)
|
|
|
|
local x1, y1 = m:transform_point(x, y)
|
2016-02-07 14:13:43 +01:00
|
|
|
local x2, y2, w2, h2 = _hierarchy:get_draw_extents()
|
|
|
|
if x1 < x2 or x1 >= x2 + w2 then
|
2015-08-12 13:12:45 +02:00
|
|
|
return
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
if y1 < y2 or y1 >= y2 + h2 then
|
2015-08-12 13:12:45 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Is (x,y) inside of this widget?
|
2016-02-07 14:13:43 +01:00
|
|
|
local width, height = _hierarchy:get_size()
|
2015-08-12 13:12:45 +02:00
|
|
|
if x1 >= 0 and y1 >= 0 and x1 <= width and y1 <= height then
|
|
|
|
-- Get the extents of this widget in the device space
|
2016-02-07 14:13:43 +01:00
|
|
|
local x3, y3, w3, h3 = matrix.transform_rectangle(_hierarchy:get_matrix_to_device(),
|
2015-08-12 13:12:45 +02:00
|
|
|
0, 0, width, height)
|
|
|
|
table.insert(result, {
|
2016-02-07 14:13:43 +01:00
|
|
|
x = x3, y = y3, width = w3, height = h3,
|
2016-07-23 06:57:29 +02:00
|
|
|
drawable = _drawable, widget = _hierarchy:get_widget(),
|
|
|
|
matrix_to_device = _hierarchy:get_matrix_to_device(),
|
|
|
|
matrix_to_parent = _hierarchy:get_matrix_to_parent(),
|
2015-08-12 13:12:45 +02:00
|
|
|
})
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, child in ipairs(_hierarchy:get_children()) do
|
|
|
|
find_widgets(_drawable, result, child, x, y)
|
2015-08-12 13:12:45 +02:00
|
|
|
end
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Find a widget by a point.
|
|
|
|
-- The drawable must have drawn itself at least once for this to work.
|
|
|
|
-- @param x X coordinate of the point
|
|
|
|
-- @param y Y coordinate of the point
|
|
|
|
-- @return A sorted table with all widgets that contain the given point. The
|
2014-05-20 13:02:13 +02:00
|
|
|
-- widgets are sorted by relevance.
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:find_widgets(x, y)
|
2015-08-12 13:12:45 +02:00
|
|
|
local result = {}
|
|
|
|
if self._widget_hierarchy then
|
|
|
|
find_widgets(self, result, self._widget_hierarchy, x, y)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
return result
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
--- Set the widget that the drawable displays
|
|
|
|
function drawable:set_widget(widget)
|
|
|
|
self.widget = widget
|
|
|
|
|
|
|
|
-- Make sure the widget gets drawn
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_relayout = true
|
2012-10-14 17:20:24 +02:00
|
|
|
self.draw()
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set the background of the drawable
|
|
|
|
-- @param c The background to use. This must either be a cairo pattern object,
|
2014-05-20 13:02:13 +02:00
|
|
|
-- nil or a string that gears.color() understands.
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:set_bg(c)
|
2016-02-07 14:13:43 +01:00
|
|
|
c = c or "#000000"
|
2016-02-08 09:37:09 +01:00
|
|
|
local t = type(c)
|
|
|
|
|
|
|
|
if t == "string" or t == "table" then
|
2012-10-14 17:20:24 +02:00
|
|
|
c = color(c)
|
|
|
|
end
|
2014-03-16 14:58:14 +01:00
|
|
|
|
|
|
|
-- If the background is completely opaque, we don't need to redraw when
|
|
|
|
-- the drawable is moved
|
|
|
|
-- XXX: This isn't needed when awesome.composite_manager_running is true,
|
|
|
|
-- but a compositing manager could stop/start and we'd have to properly
|
|
|
|
-- handle this. So for now we choose the lazy approach.
|
|
|
|
local redraw_on_move = not color.create_opaque_pattern(c)
|
|
|
|
if self._redraw_on_move ~= redraw_on_move then
|
|
|
|
self._redraw_on_move = redraw_on_move
|
|
|
|
if redraw_on_move then
|
2015-08-12 13:12:45 +02:00
|
|
|
self.drawable:connect_signal("property::x", self._do_complete_repaint)
|
|
|
|
self.drawable:connect_signal("property::y", self._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
else
|
2015-08-12 13:12:45 +02:00
|
|
|
self.drawable:disconnect_signal("property::x", self._do_complete_repaint)
|
|
|
|
self.drawable:disconnect_signal("property::y", self._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
self.background_color = c
|
2015-08-12 13:12:45 +02:00
|
|
|
self._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2016-02-08 09:37:09 +01:00
|
|
|
--- Set the background image of the drawable
|
|
|
|
-- If `image` is a function, it will be called with `(context, cr, width, height)`
|
|
|
|
-- as arguments. Any other arguments passed to this method will be appended.
|
|
|
|
-- @param image A background image or a function
|
|
|
|
function drawable:set_bgimage(image, ...)
|
2016-07-09 10:29:05 +02:00
|
|
|
if type(image) ~= "function" then
|
|
|
|
image = surface(image)
|
|
|
|
end
|
2016-02-08 09:37:09 +01:00
|
|
|
|
|
|
|
self.background_image = image
|
|
|
|
self.background_image_args = {...}
|
|
|
|
|
|
|
|
self._do_complete_repaint()
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
--- Set the foreground of the drawable
|
|
|
|
-- @param c The foreground to use. This must either be a cairo pattern object,
|
2014-05-20 13:02:13 +02:00
|
|
|
-- nil or a string that gears.color() understands.
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:set_fg(c)
|
2016-02-07 14:13:43 +01:00
|
|
|
c = c or "#FFFFFF"
|
2012-10-14 17:20:24 +02:00
|
|
|
if type(c) == "string" or type(c) == "table" then
|
|
|
|
c = color(c)
|
|
|
|
end
|
|
|
|
self.foreground_color = c
|
2015-08-12 13:12:45 +02:00
|
|
|
self._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
local function emit_difference(name, list, skip)
|
|
|
|
local function in_table(table, val)
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(table) do
|
2013-03-22 05:52:02 +01:00
|
|
|
if v.widget == val.widget then
|
2012-10-14 17:20:24 +02:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(list) do
|
2012-10-14 17:20:24 +02:00
|
|
|
if not in_table(skip, v) then
|
2013-03-22 05:52:02 +01:00
|
|
|
v.widget:emit_signal(name,v)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function handle_leave(_drawable)
|
|
|
|
emit_difference("mouse::leave", _drawable._widgets_under_mouse, {})
|
|
|
|
_drawable._widgets_under_mouse = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
local function handle_motion(_drawable, x, y)
|
|
|
|
if x < 0 or y < 0 or x > _drawable.drawable:geometry().width or y > _drawable.drawable:geometry().height then
|
|
|
|
return handle_leave(_drawable)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Build a plain list of all widgets on that point
|
|
|
|
local widgets_list = _drawable:find_widgets(x, y)
|
|
|
|
|
|
|
|
-- First, "leave" all widgets that were left
|
2013-03-22 05:52:02 +01:00
|
|
|
emit_difference("mouse::leave", _drawable._widgets_under_mouse, widgets_list)
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Then enter some widgets
|
2013-03-22 05:52:02 +01:00
|
|
|
emit_difference("mouse::enter", widgets_list, _drawable._widgets_under_mouse)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2013-03-22 05:52:02 +01:00
|
|
|
_drawable._widgets_under_mouse = widgets_list
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2012-10-31 22:12:30 +01:00
|
|
|
local function setup_signals(_drawable)
|
|
|
|
local d = _drawable.drawable
|
|
|
|
|
|
|
|
local function clone_signal(name)
|
|
|
|
-- When "name" is emitted on wibox.drawin, also emit it on wibox
|
|
|
|
d:connect_signal(name, function(_, ...)
|
|
|
|
_drawable:emit_signal(name, ...)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
clone_signal("button::press")
|
|
|
|
clone_signal("button::release")
|
|
|
|
clone_signal("mouse::enter")
|
|
|
|
clone_signal("mouse::leave")
|
|
|
|
clone_signal("mouse::move")
|
|
|
|
clone_signal("property::surface")
|
|
|
|
clone_signal("property::width")
|
|
|
|
clone_signal("property::height")
|
|
|
|
clone_signal("property::x")
|
|
|
|
clone_signal("property::y")
|
|
|
|
end
|
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
function drawable.new(d, widget_context_skeleton, drawable_name)
|
2012-10-14 17:20:24 +02:00
|
|
|
local ret = object()
|
|
|
|
ret.drawable = d
|
2015-08-08 13:13:47 +02:00
|
|
|
ret._widget_context_skeleton = widget_context_skeleton
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._need_complete_repaint = true
|
|
|
|
ret._need_relayout = true
|
|
|
|
ret._dirty_area = cairo.Region.create()
|
2012-10-31 22:12:30 +01:00
|
|
|
setup_signals(ret)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
for k, v in pairs(drawable) do
|
|
|
|
if type(v) == "function" then
|
|
|
|
ret[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Only redraw a drawable once, even when we get told to do so multiple times.
|
|
|
|
ret._redraw_pending = false
|
|
|
|
ret._do_redraw = function()
|
|
|
|
ret._redraw_pending = false
|
|
|
|
do_redraw(ret)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Connect our signal when we need a redraw
|
|
|
|
ret.draw = function()
|
|
|
|
if not ret._redraw_pending then
|
2015-01-11 10:58:49 +01:00
|
|
|
timer.delayed_call(ret._do_redraw)
|
2012-10-14 17:20:24 +02:00
|
|
|
ret._redraw_pending = true
|
|
|
|
end
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._do_complete_repaint = function()
|
|
|
|
ret._need_complete_repaint = true
|
|
|
|
ret:draw()
|
|
|
|
end
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
drawables_draw[ret.draw] = true
|
|
|
|
drawables_force_complete_repaint[ret._do_complete_repaint] = true
|
|
|
|
|
|
|
|
-- Do a full redraw if the surface changes (the new surface has no content yet)
|
2015-08-12 13:12:45 +02:00
|
|
|
d:connect_signal("property::surface", ret._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
-- Do a normal redraw when the drawable moves. This will likely do nothing
|
|
|
|
-- in most cases, but it makes us do a complete repaint when we are moved to
|
|
|
|
-- a different screen.
|
|
|
|
d:connect_signal("property::x", ret.draw)
|
|
|
|
d:connect_signal("property::y", ret.draw)
|
|
|
|
|
2014-03-16 14:58:14 +01:00
|
|
|
-- Currently we aren't redrawing on move (signals not connected).
|
|
|
|
-- :set_bg() will later recompute this.
|
|
|
|
ret._redraw_on_move = false
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
-- Set the default background
|
|
|
|
ret:set_bg(beautiful.bg_normal)
|
|
|
|
ret:set_fg(beautiful.fg_normal)
|
|
|
|
|
|
|
|
-- Initialize internals
|
|
|
|
ret._widgets_under_mouse = {}
|
|
|
|
|
|
|
|
local function button_signal(name)
|
2016-02-07 14:13:43 +01:00
|
|
|
d:connect_signal(name, function(_, x, y, button, modifiers)
|
2012-10-14 17:20:24 +02:00
|
|
|
local widgets = ret:find_widgets(x, y)
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(widgets) do
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Calculate x/y inside of the widget
|
|
|
|
local lx = x - v.x
|
|
|
|
local ly = y - v.y
|
2013-03-22 05:52:02 +01:00
|
|
|
v.widget:emit_signal(name, lx, ly, button, modifiers,v)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
button_signal("button::press")
|
|
|
|
button_signal("button::release")
|
|
|
|
|
|
|
|
d:connect_signal("mouse::move", function(_, x, y) handle_motion(ret, x, y) end)
|
|
|
|
d:connect_signal("mouse::leave", function() handle_leave(ret) end)
|
|
|
|
|
2015-08-12 13:12:45 +02:00
|
|
|
-- Set up our callbacks for repaints
|
2016-02-07 14:13:43 +01:00
|
|
|
ret._redraw_callback = function(hierar, arg)
|
2016-09-23 10:18:49 +02:00
|
|
|
-- XXX: lgi will lead us into memory-corruption-land when we use an
|
|
|
|
-- object after it was GC'd. Try to detect this situation by checking if
|
|
|
|
-- the drawable is still valid. This is only a weak indication, but it
|
|
|
|
-- seems to be the best that we can do. The problem is that the drawable
|
|
|
|
-- could not yet be GC'd, but is pending finalisation, while the
|
|
|
|
-- cairo.Region below was already GC'd. This would still lead to corruption.
|
|
|
|
if not ret.drawable.valid then
|
|
|
|
return
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
if ret._widget_hierarchy_callback_arg ~= arg then
|
|
|
|
return
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
local m = hierar:get_matrix_to_device()
|
|
|
|
local x, y, width, height = matrix.transform_rectangle(m, hierar:get_draw_extents())
|
2015-08-12 13:12:45 +02:00
|
|
|
local x1, y1 = math.floor(x), math.floor(y)
|
|
|
|
local x2, y2 = math.ceil(x + width), math.ceil(y + height)
|
|
|
|
ret._dirty_area:union_rectangle(cairo.RectangleInt{
|
|
|
|
x = x1, y = y1, width = x2 - x1, height = y2 - y1
|
|
|
|
})
|
|
|
|
ret:draw()
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
ret._layout_callback = function(_, arg)
|
2015-08-12 13:12:45 +02:00
|
|
|
if ret._widget_hierarchy_callback_arg ~= arg then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ret._need_relayout = true
|
|
|
|
ret:draw()
|
|
|
|
end
|
|
|
|
|
2015-07-29 21:12:41 +02:00
|
|
|
-- Add __tostring method to metatable.
|
|
|
|
ret.drawable_name = drawable_name or object.modulename(3)
|
|
|
|
local mt = {}
|
|
|
|
local orig_string = tostring(ret)
|
2016-02-07 14:13:43 +01:00
|
|
|
mt.__tostring = function()
|
2015-07-29 21:12:41 +02:00
|
|
|
return string.format("%s (%s)", ret.drawable_name, orig_string)
|
|
|
|
end
|
|
|
|
ret = setmetatable(ret, mt)
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Make sure the drawable is drawn at least once
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2012-11-03 19:43:31 +01:00
|
|
|
-- Redraw all drawables when the wallpaper changes
|
|
|
|
capi.awesome.connect_signal("wallpaper_changed", function()
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
for k in pairs(drawables_force_complete_repaint) do
|
2012-11-03 19:43:31 +01:00
|
|
|
k()
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
-- Give drawables a chance to react to screen changes
|
|
|
|
local function draw_all()
|
|
|
|
for k in pairs(drawables_draw) do
|
|
|
|
k()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
screen.connect_signal("property::geometry", draw_all)
|
|
|
|
screen.connect_signal("added", draw_all)
|
|
|
|
screen.connect_signal("removed", draw_all)
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
return setmetatable(drawable, { __call = function(_, ...) return drawable.new(...) end })
|
|
|
|
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|