From 713bdb0b737f9c4b553a5e06ce3ad1bb435e70dd Mon Sep 17 00:00:00 2001 From: Simon Lees Date: Fri, 28 Apr 2023 18:00:25 +0930 Subject: [PATCH 1/4] Implement Widget Rendering Sandboxing This uses cairo_surface_create_for_rectangle to give each widget it's own cairo_t, this means that Widgets can no longer draw outside the current boundry that is set for them. cairo_surface_create_for_rectangle was added to conky in v1.19.1 --- src/widget.lua | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/widget.lua b/src/widget.lua index 234380a..625cf36 100644 --- a/src/widget.lua +++ b/src/widget.lua @@ -94,14 +94,17 @@ function Renderer:layout() local background_widgets = {} self._update_widgets = {} self._render_widgets = {} - for widget, x, y in util.imap(unpack, widgets) do - local matrix = cairo_matrix_t:create() - cairo_matrix_init_translate(matrix, floor(x), floor(y)) + for widget, x, y, _width, _height in util.imap(unpack, widgets) do if widget.render_background then - table.insert(background_widgets, {widget, matrix}) + local wsr = cairo_surface_create_for_rectangle(self._background_surface, + floor(x),floor(y),floor(_width),floor(_height)) + table.insert(background_widgets, {widget, wsr}) end if widget.render then - table.insert(self._render_widgets, {widget, matrix}) + local wsr = cairo_surface_create_for_rectangle(self._background_surface, + floor(x),floor(y),floor(_width),floor(_height)) + local wcr = cairo_create(wsr) + table.insert(self._render_widgets, {widget, wsr}) end if widget.update then table.insert(self._update_widgets, widget) @@ -116,12 +119,13 @@ function Renderer:layout() cairo_paint(cr) cairo_restore(cr) - cairo_save(cr) - for widget, matrix in util.imap(unpack, background_widgets) do - cairo_set_matrix(cr, matrix) - widget:render_background(cr) + for widget, wsr in util.imap(unpack, background_widgets) do + local wcr = cairo_create(wsr) + cairo_save(wcr) + widget:render_background(wcr) + cairo_restore(wcr) + cairo_destroy(wcr) end - cairo_restore(cr) if DEBUG then local version_info = table.concat{"conky ", conky_version, @@ -164,9 +168,21 @@ end --- Render to the given context -- @tparam cairo_t cr function Renderer:render(cr) - for widget, matrix in util.imap(unpack, self._render_widgets) do - cairo_set_matrix(cr, matrix) - widget:render(cr) + for widget, wsr in util.imap(unpack, self._render_widgets) do + local wcr = cairo_create(wsr) + cairo_save(wcr) + cairo_set_source_rgba (wcr, 0, 0, 0, 0); + cairo_set_operator (wcr, CAIRO_OPERATOR_SOURCE); + cairo_paint(wcr) + -- Unfortunately the background gets cleared and needs to be + -- redrawn otherwise the widget draws the new content over the old + if widget.render_background then + cairo_save(wcr) + widget:render_background(wcr) + cairo_restore(wcr) + end + widget:render(wcr) + cairo_destroy(wcr) end end From 6a60864a3ee09c201197eedc92e1742b84bdd127 Mon Sep 17 00:00:00 2001 From: Simon Lees Date: Fri, 28 Apr 2023 16:32:36 +0930 Subject: [PATCH 2/4] Examples: Now need to load the lua_draw_hook_pre --- examples/cpu.lua | 1 + examples/graphs.lua | 1 + examples/text.lua | 1 + 3 files changed, 3 insertions(+) diff --git a/examples/cpu.lua b/examples/cpu.lua index dbf00f8..39dc249 100644 --- a/examples/cpu.lua +++ b/examples/cpu.lua @@ -71,6 +71,7 @@ local conkyrc = conky or {} conkyrc.config = { lua_load = script_dir .. "cpu.lua", lua_startup_hook = "conky_setup", + lua_draw_hook_pre = "conky_paint_background", lua_draw_hook_post = "conky_update", update_interval = 1, diff --git a/examples/graphs.lua b/examples/graphs.lua index 0a89399..1b2b18b 100644 --- a/examples/graphs.lua +++ b/examples/graphs.lua @@ -52,6 +52,7 @@ local conkyrc = conky or {} conkyrc.config = { lua_load = script_dir .. "graphs.lua", lua_startup_hook = "conky_setup", + lua_draw_hook_pre = "conky_paint_background", lua_draw_hook_post = "conky_update", update_interval = 1, diff --git a/examples/text.lua b/examples/text.lua index aa8b492..15b1ab7 100644 --- a/examples/text.lua +++ b/examples/text.lua @@ -73,6 +73,7 @@ local conkyrc = conky or {} conkyrc.config = { lua_load = script_dir .. "text.lua", lua_startup_hook = "conky_setup", + lua_draw_hook_pre = "conky_paint_background", lua_draw_hook_post = "conky_update", update_interval = 1, From 07bac2826ee6ab4420d558c6c3dfc8c88b328a01 Mon Sep 17 00:00:00 2001 From: Simon Lees Date: Fri, 5 May 2023 19:05:00 +0930 Subject: [PATCH 3/4] Improve Background Widget Rendering * Now paint the backgrounds in the pre section. * Unfortunatly we need to clear and repaint all backgrounds every cycle, unless we want to start tracking which widgets overlap others. --- src/widget.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/widget.lua b/src/widget.lua index 625cf36..8d52913 100644 --- a/src/widget.lua +++ b/src/widget.lua @@ -91,7 +91,7 @@ function Renderer:layout() local widgets = self._root:layout(self._width, self._height) or {} table.insert(widgets, 1, {self._root, 0, 0, self._width, self._height}) - local background_widgets = {} + self._background_widgets = {} self._update_widgets = {} self._render_widgets = {} for widget, x, y, _width, _height in util.imap(unpack, widgets) do @@ -119,7 +119,7 @@ function Renderer:layout() cairo_paint(cr) cairo_restore(cr) - for widget, wsr in util.imap(unpack, background_widgets) do + for widget, wsr in util.imap(unpack, self._background_widgets) do local wcr = cairo_create(wsr) cairo_save(wcr) widget:render_background(wcr) @@ -163,6 +163,19 @@ end function Renderer:paint_background(cr) cairo_set_source_surface(cr, self._background_surface, 0, 0) cairo_paint(cr) + for widget, wsr in util.imap(unpack, self._background_widgets) do + local wcr = cairo_create(wsr) + -- clear surface + cairo_save(wcr) + cairo_set_source_rgba(wcr, 0, 0, 0, 0) + cairo_set_operator(wcr, CAIRO_OPERATOR_SOURCE) + cairo_paint(wcr) + cairo_restore(wcr) + cairo_save(wcr) + widget:render_background(wcr) + cairo_restore(wcr) + cairo_destroy(wcr) + end end --- Render to the given context From 06cbb8c3b66c9e83f16c246d22c6f8406b45d1db Mon Sep 17 00:00:00 2001 From: Simon Lees Date: Tue, 9 May 2023 10:50:29 +0930 Subject: [PATCH 4/4] Use a separate surface for widget backgrounds Rather then re rendering widget backgrounds every update they are now rendered to a separate surface which is remerged with the main surface when it is cleared. --- src/widget.lua | 79 +++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/src/widget.lua b/src/widget.lua index 8d52913..9b225c8 100644 --- a/src/widget.lua +++ b/src/widget.lua @@ -82,6 +82,9 @@ function Renderer:init(args) self._background_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, args.width, args.height) + self._main_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + args.width, + args.height) end --- Layout all Widgets and cache their backgrounds. @@ -98,10 +101,10 @@ function Renderer:layout() if widget.render_background then local wsr = cairo_surface_create_for_rectangle(self._background_surface, floor(x),floor(y),floor(_width),floor(_height)) - table.insert(background_widgets, {widget, wsr}) + table.insert(self._background_widgets, {widget, wsr}) end if widget.render then - local wsr = cairo_surface_create_for_rectangle(self._background_surface, + local wsr = cairo_surface_create_for_rectangle(self._main_surface, floor(x),floor(y),floor(_width),floor(_height)) local wcr = cairo_create(wsr) table.insert(self._render_widgets, {widget, wsr}) @@ -111,14 +114,15 @@ function Renderer:layout() end end - local cr = cairo_create(self._background_surface) + local bcr = cairo_create(self._background_surface) -- clear surface - cairo_save(cr) - cairo_set_source_rgba(cr, 0, 0, 0, 0) - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE) - cairo_paint(cr) - cairo_restore(cr) + cairo_save(bcr) + cairo_set_source_rgba(bcr, 0, 0, 0, 0) + cairo_set_operator(bcr, CAIRO_OPERATOR_SOURCE) + cairo_paint(bcr) + cairo_restore(bcr) + -- render to backgrounds to surface for widget, wsr in util.imap(unpack, self._background_widgets) do local wcr = cairo_create(wsr) cairo_save(wcr) @@ -131,21 +135,21 @@ function Renderer:layout() local version_info = table.concat{"conky ", conky_version, " ", _VERSION, " cairo ", cairo_version_string()} - cairo_set_source_rgba(cr, 1, 0, 0, 1) - ch.set_font(cr, "Ubuntu", 8) - ch.write_left(cr, 0, 8, version_info) + cairo_set_source_rgba(bcr, 1, 0, 0, 1) + bcr.set_font(bcr, "Ubuntu", 8) + bcr.write_left(bcr, 0, 8, version_info) for _, x, y, width, height in util.imap(unpack, widgets) do if width * height ~= 0 then - cairo_rectangle(cr, x, y, width, height) + cairo_rectangle(bcr, x, y, width, height) end end - cairo_set_line_width(cr, 1) - cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE) - cairo_set_source_rgba(cr, 1, 0, 0, 0.33) - cairo_stroke(cr) + cairo_set_line_width(bcr, 1) + cairo_set_antialias(bcr, CAIRO_ANTIALIAS_NONE) + cairo_set_source_rgba(bcr, 1, 0, 0, 0.33) + cairo_stroke(bcr) end - cairo_destroy(cr) + cairo_destroy(bcr) end --- Update all Widgets @@ -161,42 +165,33 @@ function Renderer:update(update_count) end function Renderer:paint_background(cr) - cairo_set_source_surface(cr, self._background_surface, 0, 0) + cairo_set_source_surface(cr, self._main_surface, 0, 0) cairo_paint(cr) - for widget, wsr in util.imap(unpack, self._background_widgets) do - local wcr = cairo_create(wsr) - -- clear surface - cairo_save(wcr) - cairo_set_source_rgba(wcr, 0, 0, 0, 0) - cairo_set_operator(wcr, CAIRO_OPERATOR_SOURCE) - cairo_paint(wcr) - cairo_restore(wcr) - cairo_save(wcr) - widget:render_background(wcr) - cairo_restore(wcr) - cairo_destroy(wcr) - end end --- Render to the given context -- @tparam cairo_t cr function Renderer:render(cr) + -- It doesn't render without these two lines + cairo_set_source_surface(cr, self._main_surface, 0, 0) + cairo_paint(cr) + mcr = cairo_create(self._main_surface) + cairo_save(mcr) + -- Clear previous render for transparent widgets + cairo_set_source_rgba(mcr, 0, 0, 0, 0) + cairo_set_operator(mcr, CAIRO_OPERATOR_SOURCE) + cairo_paint(mcr) + cairo_restore(mcr) + -- Overlay background surface + cairo_set_operator(mcr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(mcr, self._background_surface, 0, 0); + -- render forground widgets for widget, wsr in util.imap(unpack, self._render_widgets) do local wcr = cairo_create(wsr) - cairo_save(wcr) - cairo_set_source_rgba (wcr, 0, 0, 0, 0); - cairo_set_operator (wcr, CAIRO_OPERATOR_SOURCE); - cairo_paint(wcr) - -- Unfortunately the background gets cleared and needs to be - -- redrawn otherwise the widget draws the new content over the old - if widget.render_background then - cairo_save(wcr) - widget:render_background(wcr) - cairo_restore(wcr) - end widget:render(wcr) cairo_destroy(wcr) end + cairo_paint(mcr); end