mirror of
https://github.com/scorpion-26/gBar.git
synced 2024-11-22 03:02:49 +00:00
SNI: Initial proof of concept
Implements a rough outline of the SNI (StatusNotifierItem) d-bus protocol for tray icons. Note: This is currently *very* WIP Full implementation will close https://github.com/scorpion-26/gBar/issues/5
This commit is contained in:
parent
05e7635c80
commit
1241d7c87c
12 changed files with 474 additions and 1 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "thirdparty/stb"]
|
||||
path = thirdparty/stb
|
||||
url = https://github.com/nothings/stb
|
31
meson.build
31
meson.build
|
@ -23,6 +23,28 @@ ext_workspace_header = custom_target('generate-ext-workspace-header',
|
|||
output: ['ext-workspace-unstable-v1.h'],
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])
|
||||
|
||||
# DBus protocols
|
||||
dbus_gen = find_program('gdbus-codegen')
|
||||
sni_item_src = custom_target('generate-sni-item-src',
|
||||
input: ['protocols/sni-item.xml'],
|
||||
output: ['sni-item.c'],
|
||||
command: [dbus_gen, '--c-namespace', 'sni', '--body', '--output', '@OUTPUT@', '@INPUT@'])
|
||||
|
||||
sni_item_header = custom_target('generate-sni-item-header',
|
||||
input: ['protocols/sni-item.xml'],
|
||||
output: ['sni-item.h'],
|
||||
command: [dbus_gen, '--c-namespace', 'sni', '--header', '--output', '@OUTPUT@', '@INPUT@'])
|
||||
|
||||
sni_watcher_src = custom_target('generate-sni-watcher-src',
|
||||
input: ['protocols/sni-watcher.xml'],
|
||||
output: ['sni-watcher.c'],
|
||||
command: [dbus_gen, '--c-namespace', 'sni', '--body', '--output', '@OUTPUT@', '@INPUT@'])
|
||||
|
||||
sni_watcher_header = custom_target('generate-sni-watcher-header',
|
||||
input: ['protocols/sni-watcher.xml'],
|
||||
output: ['sni-watcher.h'],
|
||||
command: [dbus_gen, '--c-namespace', 'sni', '--header', '--output', '@OUTPUT@', '@INPUT@'])
|
||||
|
||||
gtk = dependency('gtk+-3.0')
|
||||
gtk_layer_shell = dependency('gtk-layer-shell-0')
|
||||
|
||||
|
@ -64,9 +86,16 @@ add_global_arguments('-DUSE_LOGFILE', language: 'cpp')
|
|||
|
||||
pulse = dependency('libpulse')
|
||||
|
||||
# stb
|
||||
stb = include_directories('thirdparty')
|
||||
|
||||
libgBar = library('gBar',
|
||||
[ ext_workspace_src,
|
||||
ext_workspace_header,
|
||||
sni_item_src,
|
||||
sni_item_header,
|
||||
sni_watcher_src,
|
||||
sni_watcher_header,
|
||||
'src/Window.cpp',
|
||||
'src/Widget.cpp',
|
||||
'src/System.cpp',
|
||||
|
@ -78,8 +107,10 @@ libgBar = library('gBar',
|
|||
'src/Config.cpp',
|
||||
'src/CSS.cpp',
|
||||
'src/Log.cpp',
|
||||
'src/SNI.cpp',
|
||||
],
|
||||
dependencies: [gtk, gtk_layer_shell, pulse, wayland_client],
|
||||
include_directories: stb,
|
||||
install: true)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
|
|
46
protocols/sni-item.xml
Normal file
46
protocols/sni-item.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.StatusNotifierItem">
|
||||
<annotation name="org.gtk.GDBus.C.Name" value="item"/>
|
||||
<property name="Category" type="s" access="read"/>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Title" type="s" access="read"/>
|
||||
<property name="Status" type="s" access="read"/>
|
||||
<property name="WindowId" type="u" access="read"/>
|
||||
<property name="IconName" type="s" access="read"/>
|
||||
<property name="IconPixmap" type="a(iiay)" access="read"/>
|
||||
<property name="OverlayIconName" type="s" access="read"/>
|
||||
<property name="OverlayIconPixmap" type="a(iiay)" access="read"/>
|
||||
<property name="AttentionIconName" type="s" access="read"/>
|
||||
<property name="AttentionIconPixmap" type="a(iiay)" access="read"/>
|
||||
<property name="AttentionMovieName" type="s" access="read"/>
|
||||
<property name="ToolTip" type="(sa(iiay)ss)" access="read"/>
|
||||
<property name="ItemIsMenu" type="b" access="read"/>
|
||||
<property name="Menu" type="o" access="read"/>
|
||||
<method name="ContextMenu">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
</method>
|
||||
<method name="Activate">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
</method>
|
||||
<method name="SecondaryActivate">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
</method>
|
||||
<method name="Scroll">
|
||||
<arg type="i" direction="in" name="delta"/>
|
||||
<arg type="s" direction="in" name="orientation"/>
|
||||
</method>
|
||||
<signal name="NewTitle"/>
|
||||
<signal name="NewIcon"/>
|
||||
<signal name="NewAttentionIcon"/>
|
||||
<signal name="NewOverlayIcon"/>
|
||||
<signal name="NewToolTip"/>
|
||||
<signal name="NewStatus">
|
||||
<arg type="s" direction="in" name="status"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
23
protocols/sni-watcher.xml
Normal file
23
protocols/sni-watcher.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.StatusNotifierWatcher">
|
||||
<annotation name="org.gtk.GDBus.C.Name" value="watcher"/>
|
||||
<property name="RegisteredStatusNotifierItems" type="as" access="read"/>
|
||||
<property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
|
||||
<property name="ProtocolVersion" type="i" access="read"/>
|
||||
<method name="RegisterStatusNotifierItem">
|
||||
<arg type="s" direction="in" name="service"/>
|
||||
</method>
|
||||
<method name="RegisterStatusNotifierHost">
|
||||
<arg type="s" direction="in" name="service"/>
|
||||
</method>
|
||||
<signal name="StatusNotifierItemRegistered">
|
||||
<arg type="s" direction="out" name="service"/>
|
||||
</signal>
|
||||
<signal name="StatusNotifierItemUnregistered">
|
||||
<arg type="s" direction="out" name="service"/>
|
||||
</signal>
|
||||
<signal name="StatusNotifierHostRegistered"/>
|
||||
</interface>
|
||||
</node>
|
|
@ -3,6 +3,7 @@
|
|||
#include "System.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "SNI.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace Bar
|
||||
|
@ -646,6 +647,8 @@ namespace Bar
|
|||
right->SetSpacing({8, false});
|
||||
right->SetHorizontalTransform({-1, true, Alignment::Right});
|
||||
{
|
||||
SNI::WidgetSNI(*right);
|
||||
|
||||
WidgetPackages(*right);
|
||||
|
||||
WidgetAudio(*right);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef USE_LOGFILE
|
||||
#define LOG(x) \
|
||||
|
|
287
src/SNI.cpp
Normal file
287
src/SNI.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
#include "SNI.h"
|
||||
#include "Log.h"
|
||||
#include "Widget.h"
|
||||
|
||||
#include <sni-watcher.h>
|
||||
#include <sni-item.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace SNI
|
||||
{
|
||||
sniWatcher* watcherSkeleton;
|
||||
guint watcherID;
|
||||
GDBusConnection* dbusConnection = nullptr;
|
||||
|
||||
guint hostID;
|
||||
|
||||
struct Item
|
||||
{
|
||||
std::string name;
|
||||
std::string object;
|
||||
size_t w;
|
||||
size_t h;
|
||||
uint8_t* iconData = nullptr;
|
||||
};
|
||||
std::vector<Item> items;
|
||||
|
||||
// Gtk stuff, TODO: Allow more than one instance
|
||||
// Simply removing the gtk_drawing_areas doesn't trigger proper redrawing
|
||||
// HACK: Make an outer permanent and an inner box, which will be deleted and readded
|
||||
Widget* parentBox;
|
||||
Widget* iconBox;
|
||||
|
||||
// SNI implements the GTK-Thingies itself internally
|
||||
static void InvalidateWidget()
|
||||
{
|
||||
parentBox->RemoveChild(iconBox);
|
||||
|
||||
auto container = Widget::Create<Box>();
|
||||
iconBox = container.get();
|
||||
for (auto& item : items)
|
||||
{
|
||||
if (item.iconData)
|
||||
{
|
||||
auto texture = Widget::Create<Texture>();
|
||||
texture->SetHorizontalTransform({32, true, Alignment::Fill});
|
||||
texture->SetBuf(item.w, item.h, item.iconData);
|
||||
iconBox->AddChild(std::move(texture));
|
||||
}
|
||||
}
|
||||
parentBox->AddChild(std::move(container));
|
||||
}
|
||||
|
||||
void WidgetSNI(Widget& parent)
|
||||
{
|
||||
// Add parent box
|
||||
auto box = Widget::Create<Box>();
|
||||
auto container = Widget::Create<Box>();
|
||||
iconBox = container.get();
|
||||
parentBox = box.get();
|
||||
InvalidateWidget();
|
||||
box->AddChild(std::move(container));
|
||||
parent.AddChild(std::move(box));
|
||||
}
|
||||
|
||||
static Item CreateItem(std::string&& name, std::string&& object)
|
||||
{
|
||||
Item item{};
|
||||
item.name = name;
|
||||
item.object = object;
|
||||
auto getProperty = [&](const char* prop) -> GVariant*
|
||||
{
|
||||
GError* err = nullptr;
|
||||
GVariant* params[2];
|
||||
params[0] = g_variant_new_string("org.kde.StatusNotifierItem");
|
||||
params[1] = g_variant_new_string(prop);
|
||||
GVariant* param = g_variant_new_tuple(params, 2);
|
||||
GVariant* res = g_dbus_connection_call_sync(dbusConnection, name.c_str(), object.c_str(), "org.freedesktop.DBus.Properties", "Get", param,
|
||||
G_VARIANT_TYPE("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &err);
|
||||
if (err)
|
||||
{
|
||||
g_error_free(err);
|
||||
return nullptr;
|
||||
}
|
||||
// There's probably a better method than to use 3 variants
|
||||
// g_variant_unref(params[0]);
|
||||
// g_variant_unref(params[1]);
|
||||
// g_variant_unref(param);
|
||||
return res;
|
||||
};
|
||||
GVariant* iconPixmap = getProperty("IconPixmap");
|
||||
if (iconPixmap)
|
||||
{
|
||||
// Only get first item
|
||||
GVariant* arr = nullptr;
|
||||
g_variant_get(iconPixmap, "(v)", &arr);
|
||||
|
||||
GVariantIter* arrIter = nullptr;
|
||||
g_variant_get(arr, "a(iiay)", &arrIter);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GVariantIter* data = nullptr;
|
||||
g_variant_iter_next(arrIter, "(iiay)", &width, &height, &data);
|
||||
|
||||
LOG(width);
|
||||
LOG(height);
|
||||
item.w = width;
|
||||
item.h = height;
|
||||
item.iconData = new uint8_t[width * height * 4];
|
||||
|
||||
uint8_t px = 0;
|
||||
int i = 0;
|
||||
while (g_variant_iter_next(data, "y", &px))
|
||||
{
|
||||
item.iconData[i] = px;
|
||||
i++;
|
||||
}
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
struct Px
|
||||
{
|
||||
// This should be bgra...
|
||||
// Since source is ARGB32 in network order(=big-endian)
|
||||
// and x86 Linux is little-endian, we *should* swap b and r...
|
||||
uint8_t a, r, g, b;
|
||||
};
|
||||
Px& pixel = ((Px*)item.iconData)[i];
|
||||
// Swap to create rgba
|
||||
pixel = {pixel.r, pixel.g, pixel.b, pixel.a};
|
||||
}
|
||||
|
||||
g_variant_iter_free(data);
|
||||
g_variant_iter_free(arrIter);
|
||||
g_variant_unref(arr);
|
||||
g_variant_unref(iconPixmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get icon theme path
|
||||
GVariant* themePathVariant = getProperty("IconThemePath"); // Not defined by freedesktop, I think ayatana does this...
|
||||
GVariant* iconNameVariant = getProperty("IconName");
|
||||
|
||||
std::string iconPath;
|
||||
if (themePathVariant && iconNameVariant)
|
||||
{
|
||||
// Why GLib?
|
||||
GVariant* themePathStr = nullptr;
|
||||
g_variant_get(themePathVariant, "(v)", &themePathStr);
|
||||
GVariant* iconNameStr = nullptr;
|
||||
g_variant_get(iconNameVariant, "(v)", &iconNameStr);
|
||||
|
||||
const char* themePath = g_variant_get_string(themePathStr, nullptr);
|
||||
const char* iconName = g_variant_get_string(iconNameStr, nullptr);
|
||||
iconPath = std::string(themePath) + "/" + iconName + ".png"; // TODO: Find out if this is always png
|
||||
|
||||
g_variant_unref(themePathVariant);
|
||||
g_variant_unref(themePathStr);
|
||||
g_variant_unref(iconNameVariant);
|
||||
g_variant_unref(iconNameStr);
|
||||
}
|
||||
else if (iconNameVariant)
|
||||
{
|
||||
GVariant* iconNameStr = nullptr;
|
||||
g_variant_get(iconNameVariant, "(v)", &iconNameStr);
|
||||
|
||||
const char* iconName = g_variant_get_string(iconNameStr, nullptr);
|
||||
iconPath = std::string(iconName);
|
||||
|
||||
g_variant_unref(iconNameVariant);
|
||||
g_variant_unref(iconNameStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("SNI: Unknown path!");
|
||||
return item;
|
||||
}
|
||||
|
||||
int width, height, channels;
|
||||
stbi_uc* pixels = stbi_load(iconPath.c_str(), &width, &height, &channels, STBI_rgb_alpha);
|
||||
if (!pixels)
|
||||
{
|
||||
LOG("SNI: Cannot open " << iconPath);
|
||||
return item;
|
||||
}
|
||||
item.w = width;
|
||||
item.h = height;
|
||||
item.iconData = new uint8_t[width * height * 4];
|
||||
// Already rgba32
|
||||
memcpy(item.iconData, pixels, width * height * 4);
|
||||
stbi_image_free(pixels);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// Methods
|
||||
static void RegisterItem(sniWatcher*, GDBusMethodInvocation* invocation, const char* service)
|
||||
{
|
||||
std::string name;
|
||||
std::string object;
|
||||
if (strncmp(service, "/", 1) == 0)
|
||||
{
|
||||
// service is object (used by e.g. ayatana -> steam, discord)
|
||||
object = service;
|
||||
name = g_dbus_method_invocation_get_sender(invocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// service is bus name (used by e.g. Telegram)
|
||||
name = service;
|
||||
object = "/StatusNotifierItem";
|
||||
}
|
||||
auto it = std::find_if(items.begin(), items.end(), [&](const Item& item)
|
||||
{
|
||||
return item.name == name && item.object == object;
|
||||
});
|
||||
if (it != items.end())
|
||||
{
|
||||
LOG("Rejecting " << name << " " << object);
|
||||
return;
|
||||
}
|
||||
// TODO: Add mechanism to remove items
|
||||
LOG("SNI: Registered Item " << name << " " << object);
|
||||
Item item = CreateItem(std::move(name), std::move(object));
|
||||
items.push_back(std::move(item));
|
||||
InvalidateWidget();
|
||||
}
|
||||
static void RegisterHost(sniWatcher*, GDBusMethodInvocation*, const char*)
|
||||
{
|
||||
LOG("TODO: Implement RegisterHost!");
|
||||
}
|
||||
|
||||
// Signals
|
||||
static void ItemRegistered(sniWatcher*, const char*, void*)
|
||||
{
|
||||
// Don't care, since watcher and host will always be from gBar (at least for now)
|
||||
}
|
||||
static void ItemUnregistered(sniWatcher*, const char*, void*)
|
||||
{
|
||||
// Don't care, since watcher and host will always be from gBar (at least for now)
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
auto busAcquired = [](GDBusConnection* connection, const char*, void*)
|
||||
{
|
||||
GError* err = nullptr;
|
||||
g_dbus_interface_skeleton_export((GDBusInterfaceSkeleton*)watcherSkeleton, connection, "/StatusNotifierWatcher", &err);
|
||||
if (err)
|
||||
{
|
||||
LOG("Failed to connect to dbus! Error: " << err->message);
|
||||
g_error_free(err);
|
||||
return;
|
||||
}
|
||||
dbusConnection = connection;
|
||||
|
||||
// Connect methods and signals
|
||||
g_signal_connect(watcherSkeleton, "handle-register-status-notifier-item", G_CALLBACK(RegisterItem), nullptr);
|
||||
g_signal_connect(watcherSkeleton, "handle-register-status-notifier-host", G_CALLBACK(RegisterHost), nullptr);
|
||||
|
||||
g_signal_connect(watcherSkeleton, "status-notifier-item-registered", G_CALLBACK(ItemRegistered), nullptr);
|
||||
g_signal_connect(watcherSkeleton, "status-notifier-item-unregistered", G_CALLBACK(ItemUnregistered), nullptr);
|
||||
|
||||
// Host is always available
|
||||
sni_watcher_set_is_status_notifier_host_registered(watcherSkeleton, true);
|
||||
};
|
||||
auto emptyCallback = [](GDBusConnection*, const char*, void*) {};
|
||||
auto lostName = [](GDBusConnection*, const char*, void*)
|
||||
{
|
||||
LOG("Lost name!");
|
||||
};
|
||||
auto flags = G_BUS_NAME_OWNER_FLAGS_REPLACE | G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
|
||||
g_bus_own_name(G_BUS_TYPE_SESSION, "org.kde.StatusNotifierWatcher", (GBusNameOwnerFlags)flags, +busAcquired, +emptyCallback, +lostName,
|
||||
nullptr, nullptr);
|
||||
watcherSkeleton = sni_watcher_skeleton_new();
|
||||
|
||||
std::string hostName = "org.kde.StatusNotifierHost-" + std::to_string(getpid());
|
||||
g_bus_own_name(G_BUS_TYPE_SESSION, hostName.c_str(), (GBusNameOwnerFlags)flags, +emptyCallback, +emptyCallback, +emptyCallback, nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void Shutdown() {}
|
||||
}
|
8
src/SNI.h
Normal file
8
src/SNI.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
class Widget;
|
||||
namespace SNI
|
||||
{
|
||||
void Init();
|
||||
void WidgetSNI(Widget& parent);
|
||||
void Shutdown();
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include "PulseAudio.h"
|
||||
#include "Workspaces.h"
|
||||
#include "Config.h"
|
||||
#include "SNI.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
|
@ -646,6 +647,8 @@ namespace System
|
|||
|
||||
PulseAudio::Init();
|
||||
|
||||
SNI::Init();
|
||||
|
||||
CheckNetwork();
|
||||
}
|
||||
void FreeResources()
|
||||
|
@ -662,6 +665,8 @@ namespace System
|
|||
#ifdef WITH_BLUEZ
|
||||
StopBTScan();
|
||||
#endif
|
||||
SNI::Shutdown();
|
||||
|
||||
Logging::Shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,10 +122,32 @@ void Widget::RemoveChild(size_t idx)
|
|||
if (m_Widget)
|
||||
{
|
||||
auto& child = *m_Childs[idx];
|
||||
gtk_container_remove((GtkContainer*)child.m_Widget, m_Widget);
|
||||
gtk_container_remove((GtkContainer*)m_Widget, child.m_Widget);
|
||||
child.m_Widget = nullptr;
|
||||
}
|
||||
m_Childs.erase(m_Childs.begin() + idx);
|
||||
}
|
||||
void Widget::RemoveChild(Widget* widget)
|
||||
{
|
||||
auto it = std::find_if(m_Childs.begin(), m_Childs.end(),
|
||||
[&](std::unique_ptr<Widget>& other)
|
||||
{
|
||||
return other.get() == widget;
|
||||
});
|
||||
if (it != m_Childs.end())
|
||||
{
|
||||
if (m_Widget)
|
||||
{
|
||||
gtk_container_remove((GtkContainer*)m_Widget, it->get()->m_Widget);
|
||||
it->get()->m_Widget = nullptr;
|
||||
}
|
||||
m_Childs.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Invalid child!");
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::SetVisible(bool visible)
|
||||
{
|
||||
|
@ -475,6 +497,30 @@ void NetworkSensor::Draw(cairo_t* cr)
|
|||
gdk_rgba_free(colDown);
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (m_Pixbuf)
|
||||
g_free(m_Pixbuf);
|
||||
if (m_Bytes)
|
||||
g_free(m_Bytes);
|
||||
}
|
||||
|
||||
void Texture::SetBuf(size_t width, size_t height, uint8_t* buf)
|
||||
{
|
||||
m_Width = width;
|
||||
m_Height = height;
|
||||
m_Bytes = g_bytes_new(buf, m_Width * m_Height * 4);
|
||||
m_Pixbuf = gdk_pixbuf_new_from_bytes((GBytes*)m_Bytes, GDK_COLORSPACE_RGB, true, 8, m_Width, m_Height, m_Width * 4);
|
||||
}
|
||||
|
||||
void Texture::Draw(cairo_t* cr)
|
||||
{
|
||||
// TODO: W + H
|
||||
cairo_rectangle(cr, 0.f, 0.f, 32.f, 32.f);
|
||||
gdk_cairo_set_source_pixbuf(cr, m_Pixbuf, 0, 0);
|
||||
cairo_fill(cr);
|
||||
}
|
||||
|
||||
void Revealer::SetTransition(Transition transition)
|
||||
{
|
||||
m_Transition = transition;
|
||||
|
|
19
src/Widget.h
19
src/Widget.h
|
@ -116,6 +116,7 @@ public:
|
|||
|
||||
void AddChild(std::unique_ptr<Widget>&& widget);
|
||||
void RemoveChild(size_t idx);
|
||||
void RemoveChild(Widget* widget);
|
||||
|
||||
std::vector<std::unique_ptr<Widget>>& GetWidgets() { return m_Childs; }
|
||||
|
||||
|
@ -263,6 +264,24 @@ private:
|
|||
std::unique_ptr<Box> contextDown;
|
||||
};
|
||||
|
||||
class Texture : public CairoArea
|
||||
{
|
||||
public:
|
||||
Texture() = default;
|
||||
virtual ~Texture();
|
||||
|
||||
// Non-Owning, ARGB32
|
||||
void SetBuf(size_t width, size_t height, uint8_t* buf);
|
||||
|
||||
private:
|
||||
void Draw(cairo_t* cr) override;
|
||||
|
||||
size_t m_Width;
|
||||
size_t m_Height;
|
||||
GBytes* m_Bytes;
|
||||
GdkPixbuf* m_Pixbuf;
|
||||
};
|
||||
|
||||
class Revealer : public Widget
|
||||
{
|
||||
public:
|
||||
|
|
1
thirdparty/stb
vendored
Submodule
1
thirdparty/stb
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5736b15f7ea0ffb08dd38af21067c314d6a3aae9
|
Loading…
Reference in a new issue