2012-10-14 17:14:22 +02:00
|
|
|
/*
|
|
|
|
* drawable.c - drawable functions
|
|
|
|
*
|
|
|
|
* Copyright © 2008-2009 Julien Danjou <julien@danjou.info>
|
|
|
|
* Copyright © 2010-2012 Uli Schlachter <psychon@znc.in>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-12-21 06:54:15 +01:00
|
|
|
/** Low-level API to allow Cairo to draw on clients and wiboxes.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
|
|
|
* @author Uli Schlachter <psychon@znc.in>
|
|
|
|
* @copyright 2012 Uli Schlachter
|
2019-06-06 08:40:00 +02:00
|
|
|
* @coreclassmod drawable
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2012-10-14 17:14:22 +02:00
|
|
|
#include "drawable.h"
|
2014-03-30 20:07:48 +02:00
|
|
|
#include "common/luaobject.h"
|
|
|
|
#include "globalconf.h"
|
2012-10-14 17:14:22 +02:00
|
|
|
|
|
|
|
#include <cairo-xcb.h>
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Drawable object.
|
|
|
|
*
|
2022-08-22 08:02:27 +02:00
|
|
|
* @property image
|
|
|
|
* @tparam image image
|
|
|
|
* @propertydefault Autogenerated.
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2016-06-04 17:39:14 +02:00
|
|
|
/**
|
|
|
|
* @signal button::press
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal button::release
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal mouse::enter
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal mouse::leave
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal mouse::move
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::geometry
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::height
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::width
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::x
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::y
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @signal property::surface
|
|
|
|
*/
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Get the number of instances.
|
|
|
|
*
|
|
|
|
* @return The number of drawable objects alive.
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct instances
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2022-07-05 10:37:14 +02:00
|
|
|
/* Set a __index metamethod for all drawable instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_index_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2022-07-05 10:37:14 +02:00
|
|
|
/* Set a __newindex metamethod for all drawable instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_newindex_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2012-10-14 17:14:22 +02:00
|
|
|
static lua_class_t drawable_class;
|
|
|
|
|
|
|
|
LUA_OBJECT_FUNCS(drawable_class, drawable_t, drawable)
|
|
|
|
|
|
|
|
drawable_t *
|
2012-10-20 22:51:52 +02:00
|
|
|
drawable_allocator(lua_State *L, drawable_refresh_callback *callback, void *data)
|
2012-10-14 17:14:22 +02:00
|
|
|
{
|
|
|
|
drawable_t *d = drawable_new(L);
|
|
|
|
d->refresh_callback = callback;
|
|
|
|
d->refresh_data = data;
|
2014-03-17 16:27:10 +01:00
|
|
|
d->refreshed = false;
|
2012-10-20 22:51:52 +02:00
|
|
|
d->surface = NULL;
|
2014-03-16 17:08:36 +01:00
|
|
|
d->pixmap = XCB_NONE;
|
2012-10-14 17:14:22 +02:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-02-20 14:09:19 +01:00
|
|
|
drawable_unset_surface(drawable_t *d)
|
2012-10-14 17:14:22 +02:00
|
|
|
{
|
2013-02-20 14:09:19 +01:00
|
|
|
cairo_surface_finish(d->surface);
|
|
|
|
cairo_surface_destroy(d->surface);
|
2014-03-16 17:08:36 +01:00
|
|
|
if (d->pixmap)
|
|
|
|
xcb_free_pixmap(globalconf.connection, d->pixmap);
|
2014-03-17 16:27:10 +01:00
|
|
|
d->refreshed = false;
|
2013-02-20 14:09:19 +01:00
|
|
|
d->surface = NULL;
|
2014-03-16 17:08:36 +01:00
|
|
|
d->pixmap = XCB_NONE;
|
2013-02-20 14:09:19 +01:00
|
|
|
}
|
2012-10-14 17:14:22 +02:00
|
|
|
|
2014-03-16 17:08:36 +01:00
|
|
|
static void
|
|
|
|
drawable_wipe(drawable_t *d)
|
2013-02-20 14:09:19 +01:00
|
|
|
{
|
|
|
|
drawable_unset_surface(d);
|
2012-10-14 17:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-12-06 10:46:45 +01:00
|
|
|
drawable_set_geometry(lua_State *L, int didx, area_t geom)
|
2012-10-14 17:14:22 +02:00
|
|
|
{
|
2014-12-06 10:46:45 +01:00
|
|
|
drawable_t *d = luaA_checkudata(L, didx, &drawable_class);
|
2012-10-14 17:14:22 +02:00
|
|
|
area_t old = d->geometry;
|
|
|
|
d->geometry = geom;
|
|
|
|
|
2023-04-10 20:18:20 +02:00
|
|
|
bool area_changed = !AREA_EQUAL(old, geom);
|
|
|
|
if (area_changed)
|
2014-03-16 17:08:36 +01:00
|
|
|
drawable_unset_surface(d);
|
2023-04-10 20:18:20 +02:00
|
|
|
if (area_changed && geom.width > 0 && geom.height > 0)
|
2014-03-16 17:08:36 +01:00
|
|
|
{
|
|
|
|
d->pixmap = xcb_generate_id(globalconf.connection);
|
|
|
|
xcb_create_pixmap(globalconf.connection, globalconf.default_depth, d->pixmap,
|
|
|
|
globalconf.screen->root, geom.width, geom.height);
|
|
|
|
d->surface = cairo_xcb_surface_create(globalconf.connection,
|
|
|
|
d->pixmap, globalconf.visual,
|
|
|
|
geom.width, geom.height);
|
2014-12-06 10:46:45 +01:00
|
|
|
luaA_object_emit_signal(L, didx, "property::surface", 0);
|
2014-03-16 17:08:36 +01:00
|
|
|
}
|
|
|
|
|
2023-04-10 20:18:20 +02:00
|
|
|
if (area_changed)
|
2015-10-14 19:40:18 +02:00
|
|
|
luaA_object_emit_signal(L, didx, "property::geometry", 0);
|
2012-10-14 17:14:22 +02:00
|
|
|
if (old.x != geom.x)
|
2014-12-06 10:46:45 +01:00
|
|
|
luaA_object_emit_signal(L, didx, "property::x", 0);
|
2012-10-14 17:14:22 +02:00
|
|
|
if (old.y != geom.y)
|
2014-12-06 10:46:45 +01:00
|
|
|
luaA_object_emit_signal(L, didx, "property::y", 0);
|
2012-10-14 17:14:22 +02:00
|
|
|
if (old.width != geom.width)
|
2014-12-06 10:46:45 +01:00
|
|
|
luaA_object_emit_signal(L, didx, "property::width", 0);
|
2012-10-14 17:14:22 +02:00
|
|
|
if (old.height != geom.height)
|
2014-12-06 10:46:45 +01:00
|
|
|
luaA_object_emit_signal(L, didx, "property::height", 0);
|
2012-10-14 17:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Get a drawable's surface
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param drawable The drawable object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_drawable_get_surface(lua_State *L, drawable_t *drawable)
|
|
|
|
{
|
2016-01-15 18:37:08 +01:00
|
|
|
if (drawable->surface)
|
|
|
|
/* Lua gets its own reference which it will have to destroy */
|
|
|
|
lua_pushlightuserdata(L, cairo_surface_reference(drawable->surface));
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
2012-10-14 17:14:22 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Refresh a drawable's content. This has to be called whenever some drawing to
|
|
|
|
* the drawable's surface has been done and should become visible.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method refresh
|
2022-07-05 10:37:14 +02:00
|
|
|
* @noreturn
|
2012-10-14 17:14:22 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_drawable_refresh(lua_State *L)
|
|
|
|
{
|
|
|
|
drawable_t *drawable = luaA_checkudata(L, 1, &drawable_class);
|
2023-08-13 10:00:45 +02:00
|
|
|
|
|
|
|
/* GC race condition where the drawin is gone, but the drawable as a
|
|
|
|
* refresh in a delayed call */
|
|
|
|
if (!drawable->refresh_callback)
|
|
|
|
return 0;
|
|
|
|
|
2014-03-17 16:27:10 +01:00
|
|
|
drawable->refreshed = true;
|
2012-10-14 17:14:22 +02:00
|
|
|
(*drawable->refresh_callback)(drawable->refresh_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Get drawable geometry. The geometry consists of x, y, width and height.
|
|
|
|
*
|
2015-07-27 01:05:01 +02:00
|
|
|
* @treturn table A table with drawable coordinates and geometry.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method geometry
|
2012-10-14 17:14:22 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_drawable_geometry(lua_State *L)
|
|
|
|
{
|
|
|
|
drawable_t *d = luaA_checkudata(L, 1, &drawable_class);
|
|
|
|
return luaA_pusharea(L, d->geometry);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
drawable_class_setup(lua_State *L)
|
|
|
|
{
|
|
|
|
static const struct luaL_Reg drawable_methods[] =
|
|
|
|
{
|
|
|
|
LUA_CLASS_METHODS(drawable)
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct luaL_Reg drawable_meta[] =
|
|
|
|
{
|
|
|
|
LUA_OBJECT_META(drawable)
|
|
|
|
LUA_CLASS_META
|
|
|
|
{ "refresh", luaA_drawable_refresh },
|
|
|
|
{ "geometry", luaA_drawable_geometry },
|
|
|
|
{ NULL, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
luaA_class_setup(L, &drawable_class, "drawable", NULL,
|
|
|
|
(lua_class_allocator_t) drawable_new,
|
|
|
|
(lua_class_collector_t) drawable_wipe, NULL,
|
|
|
|
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
|
|
|
|
drawable_methods, drawable_meta);
|
|
|
|
luaA_class_add_property(&drawable_class, "surface",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_drawable_get_surface,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2023-08-13 10:00:45 +02:00
|
|
|
void drawable_invalidate(drawable_t *d)
|
|
|
|
{
|
|
|
|
d->refresh_callback = NULL;
|
|
|
|
d->refresh_data = NULL;
|
|
|
|
}
|
|
|
|
|
2021-12-21 06:54:15 +01:00
|
|
|
/* @DOC_cobject_COMMON@ */
|
|
|
|
|
2012-10-14 17:14:22 +02:00
|
|
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|