feat: update to new nheko api

This commit is contained in:
LordMZTE 2022-10-13 00:25:40 +02:00
parent eddd3f8cee
commit 4925fdf2ca
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
7 changed files with 187 additions and 77 deletions

View file

@ -43,7 +43,6 @@ const NhdbusGen = struct {
} }
fn make(step: *std.build.Step) !void { fn make(step: *std.build.Step) !void {
_ = step;
const self = @fieldParentPtr(NhdbusGen, "step", step); const self = @fieldParentPtr(NhdbusGen, "step", step);
const b = self.lib.builder; const b = self.lib.builder;

View file

@ -1,65 +1,64 @@
<!DOCTYPE <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node> <node>
<interface name="im.nheko.Nheko"> <interface name="im.nheko.Nheko">
<method name="apiVersion"> <method name="apiVersion">
<arg type="s" direction="out" /> <arg type="s" direction="out"/>
</method> </method>
<method name="nhekoVersion"> <method name="nhekoVersion">
<arg type="s" direction="out" /> <arg type="s" direction="out"/>
</method> </method>
<method name="rooms"> <method name="rooms">
<arg type="a(sss(iiibiiay)i)" direction="out" /> <arg type="a(ssssi)" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVector&lt;nheko::dbus::RoomInfoItem&gt;"/>
value="QVector&lt;nheko::dbus::RoomInfoItem&gt;" /> </method>
<method name="image">
<arg type="(iiibiiay)" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QImage"/>
<arg name="uri" type="s" direction="in"/>
</method> </method>
<method name="activateRoom"> <method name="activateRoom">
<arg name="alias" type="s" direction="in" /> <arg name="alias" type="s" direction="in"/>
</method> </method>
<method name="joinRoom"> <method name="joinRoom">
<arg name="alias" type="s" direction="in" /> <arg name="alias" type="s" direction="in"/>
</method> </method>
<method name="directChat"> <method name="directChat">
<arg name="userId" type="s" direction="in" /> <arg name="userId" type="s" direction="in"/>
</method> </method>
</interface> </interface>
<interface name="org.freedesktop.DBus.Properties"> <interface name="org.freedesktop.DBus.Properties">
<method name="Get"> <method name="Get">
<arg name="interface_name" type="s" direction="in" /> <arg name="interface_name" type="s" direction="in"/>
<arg name="property_name" type="s" direction="in" /> <arg name="property_name" type="s" direction="in"/>
<arg name="value" type="v" direction="out" /> <arg name="value" type="v" direction="out"/>
</method> </method>
<method name="Set"> <method name="Set">
<arg name="interface_name" type="s" direction="in" /> <arg name="interface_name" type="s" direction="in"/>
<arg name="property_name" type="s" direction="in" /> <arg name="property_name" type="s" direction="in"/>
<arg name="value" type="v" direction="in" /> <arg name="value" type="v" direction="in"/>
</method> </method>
<method name="GetAll"> <method name="GetAll">
<arg name="interface_name" type="s" direction="in" /> <arg name="interface_name" type="s" direction="in"/>
<arg name="values" type="a{sv}" direction="out" /> <arg name="values" type="a{sv}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
value="QVariantMap" />
</method> </method>
<signal name="PropertiesChanged"> <signal name="PropertiesChanged">
<arg name="interface_name" type="s" direction="out" /> <arg name="interface_name" type="s" direction="out"/>
<arg name="changed_properties" type="a{sv}" <arg name="changed_properties" type="a{sv}" direction="out"/>
direction="out" /> <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" <arg name="invalidated_properties" type="as" direction="out"/>
value="QVariantMap" />
<arg name="invalidated_properties" type="as"
direction="out" />
</signal> </signal>
</interface> </interface>
<interface name="org.freedesktop.DBus.Introspectable"> <interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect"> <method name="Introspect">
<arg name="xml_data" type="s" direction="out" /> <arg name="xml_data" type="s" direction="out"/>
</method> </method>
</interface> </interface>
<interface name="org.freedesktop.DBus.Peer"> <interface name="org.freedesktop.DBus.Peer">
<method name="Ping" /> <method name="Ping"/>
<method name="GetMachineId"> <method name="GetMachineId">
<arg name="machine_uuid" type="s" direction="out" /> <arg name="machine_uuid" type="s" direction="out"/>
</method> </method>
</interface> </interface>
</node> </node>

48
src/IconStore.zig Normal file
View file

@ -0,0 +1,48 @@
const std = @import("std");
const c = @import("ffi.zig").c;
const dbus = @import("dbus.zig");
const Image = @import("Image.zig");
const Entry = union(enum) {
loading,
cached: Image,
};
data: std.StringHashMap(Entry),
const Self = @This();
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.data = std.StringHashMap(Entry).init(alloc),
};
}
pub fn deinit(self: *Self) void {
self.data.deinit();
self.* = undefined;
}
/// Will grab the image if its already cached,
/// otherwise will start a DBus request to load it, reloading the view
/// once the image is received.
pub fn get(
self: *Self,
url: [:0]const u8,
proxy: *c.nhdbus,
icon_data_alloc: std.mem.Allocator,
) !?Image {
const result = try self.data.getOrPut(url);
if (result.found_existing) {
switch (result.value_ptr.*) {
.loading => return null,
.cached => |img| return img,
}
}
std.log.info("Requesting icon `{s}`", .{url});
try dbus.requestIcon(proxy, self, url, icon_data_alloc);
result.value_ptr.* = .loading;
return null;
}

View file

@ -1,15 +1,8 @@
const c = @import("ffi.zig").c; const c = @import("ffi.zig").c;
const Image = @import("Image.zig");
/// Strings must be deallocated manually /// Strings must be deallocated manually
id: [:0]c.gchar, id: [:0]c.gchar,
alias: [:0]c.gchar, alias: [:0]c.gchar,
name: [:0]c.gchar, name: [:0]c.gchar,
image: Image, icon_url: ?[:0]c.gchar,
notifications: c_int, notifications: c_int,
const Self = @This();
pub fn deinit(self: *Self) void {
self.image.deinit();
}

View file

@ -1,10 +1,12 @@
const std = @import("std"); const std = @import("std");
const c = @import("ffi.zig").c; const c = @import("ffi.zig").c;
const Room = @import("Room.zig"); const Room = @import("Room.zig");
const IconStore = @import("IconStore.zig");
rooms: std.ArrayList(Room), rooms: std.ArrayList(Room),
rooms_lock: std.Thread.Mutex, rooms_lock: std.Thread.Mutex,
room_arena: std.heap.ArenaAllocator, room_arena: std.heap.ArenaAllocator,
icon_store: IconStore,
dbus: *c.nhdbus, dbus: *c.nhdbus,
const Self = @This(); const Self = @This();
@ -14,6 +16,7 @@ pub fn deinit(self: *Self) void {
self.room_arena.deinit(); self.room_arena.deinit();
self.rooms.deinit(); self.rooms.deinit();
self.icon_store.deinit();
c.g_object_unref(self.dbus); c.g_object_unref(self.dbus);
self.* = undefined; self.* = undefined;

View file

@ -4,6 +4,7 @@ const c = ffi.c;
const State = @import("State.zig"); const State = @import("State.zig");
const Room = @import("Room.zig"); const Room = @import("Room.zig");
const Image = @import("Image.zig"); const Image = @import("Image.zig");
const IconStore = @import("IconStore.zig");
pub fn onRoomsReceived(proxy: *c.nhdbus, res: *c.GAsyncResult, s: *State) void { pub fn onRoomsReceived(proxy: *c.nhdbus, res: *c.GAsyncResult, s: *State) void {
var rooms: ?*c.GVariant = null; var rooms: ?*c.GVariant = null;
@ -41,54 +42,29 @@ fn readRooms(rooms: *c.GVariant, s: *State) void {
var room_id: [*c]c.gchar = undefined; var room_id: [*c]c.gchar = undefined;
var room_alias: [*c]c.gchar = undefined; var room_alias: [*c]c.gchar = undefined;
var room_name: [*c]c.gchar = undefined; var room_name: [*c]c.gchar = undefined;
var icon_url: [*c]c.gchar = undefined;
var room_notifications: c_int = undefined; var room_notifications: c_int = undefined;
var img_width: c_int = undefined;
var img_height: c_int = undefined;
var img_stride: c_int = undefined;
var img_has_alpha: c_int = undefined;
var img_bit_depth: c_int = undefined;
var data_iter: *c.GVariantIter = undefined;
c.g_variant_get( c.g_variant_get(
room, room,
"(&s&s&s(iiibiiay)i)", "(&s&s&s&si)",
&room_id, &room_id,
&room_alias, &room_alias,
&room_name, &room_name,
&img_width, &icon_url,
&img_height,
&img_stride,
&img_has_alpha,
&img_bit_depth,
@as(?*anyopaque, null), // channel count
&data_iter,
&room_notifications, &room_notifications,
); );
const bytes = arena.alloc( const icon_url_s = std.mem.span(icon_url);
u8,
@intCast(usize, c.g_variant_iter_n_children(data_iter)),
) catch return;
var byte: u8 = 0;
var i: usize = 0;
while (c.g_variant_iter_next(data_iter, "y", &byte) != 0) {
bytes[i] = byte;
i += 1;
}
const r = Room{ const r = Room{
.id = arena.dupeZ(u8, std.mem.span(room_id)) catch return, .id = arena.dupeZ(u8, std.mem.span(room_id)) catch return,
.alias = arena.dupeZ(u8, std.mem.span(room_alias)) catch return, .alias = arena.dupeZ(u8, std.mem.span(room_alias)) catch return,
.name = arena.dupeZ(u8, std.mem.span(room_name)) catch return, .name = arena.dupeZ(u8, std.mem.span(room_name)) catch return,
.image = Image.init( .icon_url = if (icon_url_s.len == 0)
img_width, null
img_height, else
img_stride, arena.dupeZ(u8, icon_url_s) catch return,
img_has_alpha,
img_bit_depth,
arena.dupe(u8, bytes) catch return,
),
.notifications = room_notifications, .notifications = room_notifications,
}; };
@ -100,6 +76,89 @@ fn readRooms(rooms: *c.GVariant, s: *State) void {
std.log.info("Read {} rooms", .{s.rooms.items.len}); std.log.info("Read {} rooms", .{s.rooms.items.len});
} }
const IconUdata = struct {
url: [:0]u8,
store: *IconStore,
data_alloc: std.mem.Allocator,
};
pub fn requestIcon(
proxy: *c.nhdbus,
store: *IconStore,
url: [:0]const u8,
data_alloc: std.mem.Allocator,
) !void {
const url_d = try std.heap.c_allocator.dupeZ(u8, url);
var udata = try std.heap.c_allocator.create(IconUdata);
udata.* = .{
.url = url_d,
.store = store,
.data_alloc = data_alloc,
};
c.nhdbus__call_image(
proxy,
url.ptr,
null,
@ptrCast(c.GAsyncReadyCallback, &onIconReceived),
udata,
);
}
fn onIconReceived(proxy: *c.nhdbus, res: *c.GAsyncResult, udata: *IconUdata) void {
defer std.heap.c_allocator.destroy(udata);
defer std.heap.c_allocator.free(udata.url);
var image: ?*c.GVariant = null;
var err: ?*c.GError = null;
_ = c.nhdbus__call_image_finish(proxy, &image, res, &err);
ffi.checkGError(err) catch return;
defer c.g_variant_unref(image);
var img_width: c_int = undefined;
var img_height: c_int = undefined;
var img_stride: c_int = undefined;
var img_has_alpha: c_int = undefined;
var img_bit_depth: c_int = undefined;
var data_iter: *c.GVariantIter = undefined;
c.g_variant_get(
image,
"(iiibiiay)",
&img_width,
&img_height,
&img_stride,
&img_has_alpha,
&img_bit_depth,
@as(?*anyopaque, null), // channel count
&data_iter,
);
const data = udata.data_alloc.alloc(
u8,
@intCast(usize, c.g_variant_iter_n_children(data_iter)),
) catch return;
var byte: u8 = 0;
var i: usize = 0;
while (c.g_variant_iter_next(data_iter, "y", &byte) != 0) {
data[i] = byte;
i += 1;
}
const img = Image.init(
img_width,
img_height,
img_stride,
img_has_alpha,
img_bit_depth,
data,
);
(udata.store.data.getOrPut(udata.url) catch return).value_ptr.* = .{ .cached = img };
ffi.rofi_view_reload();
}
fn compareRoom(_: void, lhs: Room, rhs: Room) bool { fn compareRoom(_: void, lhs: Room, rhs: Room) bool {
if (lhs.notifications == rhs.notifications) { if (lhs.notifications == rhs.notifications) {
return std.ascii.lessThanIgnoreCase( return std.ascii.lessThanIgnoreCase(

View file

@ -6,6 +6,7 @@ const Mode = c.Mode;
const Image = @import("Image.zig"); const Image = @import("Image.zig");
const State = @import("State.zig"); const State = @import("State.zig");
const Room = @import("Room.zig"); const Room = @import("Room.zig");
const IconStore = @import("IconStore.zig");
pub const log = @import("glib-log").log(c, "rofi-nheko", 512); pub const log = @import("glib-log").log(c, "rofi-nheko", 512);
// log levels are handled by glib, so zig shouldn't filter // log levels are handled by glib, so zig shouldn't filter
@ -52,10 +53,6 @@ fn init(m: [*c]Mode) callconv(.C) c_int {
const state_p = std.heap.c_allocator.create(State) catch return 0; const state_p = std.heap.c_allocator.create(State) catch return 0;
state_p.rooms = std.ArrayList(Room).init(std.heap.c_allocator);
state_p.rooms_lock = .{};
state_p.room_arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
var err: ?*c.GError = null; var err: ?*c.GError = null;
const proxy = c.nhdbus__proxy_new_for_bus_sync( const proxy = c.nhdbus__proxy_new_for_bus_sync(
@ -69,6 +66,14 @@ fn init(m: [*c]Mode) callconv(.C) c_int {
ffi.checkGError(err) catch return 0; ffi.checkGError(err) catch return 0;
state_p.dbus = proxy.?; state_p.dbus = proxy.?;
state_p.* = .{
.dbus = proxy.?,
.rooms = std.ArrayList(Room).init(std.heap.c_allocator),
.rooms_lock = .{},
.room_arena = std.heap.ArenaAllocator.init(std.heap.c_allocator),
.icon_store = IconStore.init(std.heap.c_allocator),
};
c.mode_set_private_data(m, state_p); c.mode_set_private_data(m, state_p);
c.nhdbus__call_rooms( c.nhdbus__call_rooms(
@ -180,7 +185,11 @@ fn getIcon(
state(m).rooms_lock.lock(); state(m).rooms_lock.lock();
defer state(m).rooms_lock.unlock(); defer state(m).rooms_lock.unlock();
const img = &state(m).rooms.items[selected_line].image; var img = (state(m).icon_store.get(
state(m).rooms.items[selected_line].icon_url orelse return null,
state(m).dbus,
state(m).room_arena.allocator(),
) catch return null) orelse return null;
const img_surface = (img.createCairoSurface() catch return null) orelse return null; const img_surface = (img.createCairoSurface() catch return null) orelse return null;
defer c.cairo_surface_destroy(img_surface); defer c.cairo_surface_destroy(img_surface);