mirror of
https://mzte.de/git/LordMZTE/dotfiles.git
synced 2024-12-13 11:32:58 +01:00
a bunch of improvements to playtwitch
This commit is contained in:
parent
4c4139709a
commit
4ecae122c5
4 changed files with 196 additions and 117 deletions
|
@ -3,6 +3,13 @@ const c = @import("ffi.zig").c;
|
|||
const config = @import("config.zig");
|
||||
const log = std.log.scoped(.state);
|
||||
|
||||
pub const Entry = union(enum) {
|
||||
channel: ChannelEntry,
|
||||
|
||||
/// a seperator in the channel list, the optional string is a heading.
|
||||
separator: ?[]const u8,
|
||||
};
|
||||
|
||||
pub const ChannelEntry = struct {
|
||||
name: []const u8,
|
||||
comment: ?[]const u8,
|
||||
|
@ -23,7 +30,7 @@ chatty: bool,
|
|||
chatty_alive: bool,
|
||||
|
||||
/// an array of channels, composed of slices into `channels_file_data`
|
||||
channels: ?[]ChannelEntry,
|
||||
channels: ?[]Entry,
|
||||
|
||||
/// the data of the channels configuration file
|
||||
channels_file_data: ?[]u8,
|
||||
|
|
|
@ -26,16 +26,16 @@ pub fn configLoaderThread(state: *State) !void {
|
|||
defer file.close();
|
||||
|
||||
const channels_data = try file.readToEndAlloc(std.heap.c_allocator, std.math.maxInt(usize));
|
||||
var channels = std.ArrayList(State.ChannelEntry).init(std.heap.c_allocator);
|
||||
var channels = std.ArrayList(State.Entry).init(std.heap.c_allocator);
|
||||
|
||||
var channels_iter = std.mem.split(u8, channels_data, "\n");
|
||||
var channels_iter = std.mem.tokenize(u8, channels_data, "\n");
|
||||
while (channels_iter.next()) |line| {
|
||||
var line_iter = std.mem.split(u8, line, ":");
|
||||
var line_iter = std.mem.tokenize(u8, line, ":");
|
||||
|
||||
const channel = line_iter.next() orelse continue;
|
||||
const channel_trimmed = std.mem.trim(u8, channel, " \n\r");
|
||||
|
||||
if (channel_trimmed.len == 0 or channel_trimmed[0] == '#')
|
||||
if (channel_trimmed.len <= 0 or channel_trimmed[0] == '#')
|
||||
continue;
|
||||
|
||||
const comment_trimmed = blk: {
|
||||
|
@ -49,16 +49,24 @@ pub fn configLoaderThread(state: *State) !void {
|
|||
break :blk comment_trimmed;
|
||||
};
|
||||
|
||||
try channels.append(.{
|
||||
// dashes act as separator
|
||||
if (std.mem.allEqual(u8, channel_trimmed, '-')) {
|
||||
// separators can have comments to act as headings
|
||||
try channels.append(.{ .separator = comment_trimmed });
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try channels.append(.{ .channel = .{
|
||||
.name = channel_trimmed,
|
||||
.comment = comment_trimmed,
|
||||
});
|
||||
} });
|
||||
}
|
||||
|
||||
const end_time = std.time.milliTimestamp();
|
||||
|
||||
log.info(
|
||||
"Loaded {d} channels in {d}ms",
|
||||
"Loaded {d} channel items in {d}ms",
|
||||
.{ channels.items.len, end_time - start_time },
|
||||
);
|
||||
|
||||
|
|
|
@ -16,85 +16,100 @@ pub fn winContent(state: *State) !void {
|
|||
|
||||
var start: StartType = .none;
|
||||
|
||||
// Chatty checkbox
|
||||
_ = c.igCheckbox("Start Chatty", &state.chatty);
|
||||
|
||||
// Quality input
|
||||
igu.sliceText("Quality ");
|
||||
c.igSameLine(0.0, 0.0);
|
||||
|
||||
if (c.igInputText(
|
||||
"##quality_input",
|
||||
&state.quality_buf,
|
||||
state.quality_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
if (c.igBeginTable(
|
||||
"##text_inputs",
|
||||
2,
|
||||
0,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
0.0,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
defer c.igEndTable();
|
||||
|
||||
var quality_popup_pos: c.ImVec2 = undefined;
|
||||
c.igGetItemRectMin(&quality_popup_pos);
|
||||
var quality_popup_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&quality_popup_size);
|
||||
c.igTableSetupColumn("##label", c.ImGuiTableColumnFlags_WidthFixed, 85.0, 0);
|
||||
c.igTableSetupColumn("##input", 0, 0.0, 0);
|
||||
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igArrowButton("##open_quality_popup", c.ImGuiDir_Down)) {
|
||||
c.igOpenPopup_Str("quality_popup", 0);
|
||||
}
|
||||
// open popup on arrow button click
|
||||
c.igOpenPopupOnItemClick("quality_popup", 0);
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
|
||||
var btn_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&btn_size);
|
||||
// Quality input
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
igu.sliceText("Quality");
|
||||
|
||||
const preset_qualities = [_][:0]const u8{
|
||||
"best",
|
||||
"1080p60",
|
||||
"720p60",
|
||||
"480p",
|
||||
"360p",
|
||||
"worst",
|
||||
"audio_only",
|
||||
};
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
if (c.igInputText(
|
||||
"##quality_input",
|
||||
&state.quality_buf,
|
||||
state.quality_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
|
||||
quality_popup_pos.y += quality_popup_size.y;
|
||||
quality_popup_size.x += btn_size.x;
|
||||
quality_popup_size.y += 5 + (quality_popup_size.y * @intToFloat(
|
||||
f32,
|
||||
preset_qualities.len,
|
||||
));
|
||||
var quality_popup_pos: c.ImVec2 = undefined;
|
||||
c.igGetItemRectMin(&quality_popup_pos);
|
||||
var quality_popup_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&quality_popup_size);
|
||||
|
||||
c.igSetNextWindowPos(quality_popup_pos, c.ImGuiCond_Always, .{ .x = 0.0, .y = 0.0 });
|
||||
c.igSetNextWindowSize(quality_popup_size, c.ImGuiCond_Always);
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igArrowButton("##open_quality_popup", c.ImGuiDir_Down)) {
|
||||
c.igOpenPopup_Str("quality_popup", 0);
|
||||
}
|
||||
// open popup on arrow button click
|
||||
c.igOpenPopupOnItemClick("quality_popup", 0);
|
||||
|
||||
if (c.igBeginPopup("quality_popup", c.ImGuiWindowFlags_NoMove)) {
|
||||
defer c.igEndPopup();
|
||||
var btn_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&btn_size);
|
||||
|
||||
for (preset_qualities) |quality| {
|
||||
if (c.igSelectable_Bool(quality.ptr, false, 0, .{ .x = 0.0, .y = 0.0 })) {
|
||||
std.mem.set(u8, &state.quality_buf, 0);
|
||||
std.mem.copy(u8, &state.quality_buf, quality);
|
||||
const preset_qualities = [_][:0]const u8{
|
||||
"best",
|
||||
"1080p60",
|
||||
"720p60",
|
||||
"480p",
|
||||
"360p",
|
||||
"worst",
|
||||
"audio_only",
|
||||
};
|
||||
|
||||
quality_popup_pos.y += quality_popup_size.y;
|
||||
quality_popup_size.x += btn_size.x;
|
||||
quality_popup_size.y += 5 + (quality_popup_size.y * @intToFloat(
|
||||
f32,
|
||||
preset_qualities.len,
|
||||
));
|
||||
|
||||
c.igSetNextWindowPos(quality_popup_pos, c.ImGuiCond_Always, .{ .x = 0.0, .y = 0.0 });
|
||||
c.igSetNextWindowSize(quality_popup_size, c.ImGuiCond_Always);
|
||||
|
||||
if (c.igBeginPopup("quality_popup", c.ImGuiWindowFlags_NoMove)) {
|
||||
defer c.igEndPopup();
|
||||
|
||||
for (preset_qualities) |quality| {
|
||||
if (c.igSelectable_Bool(quality.ptr, false, 0, .{ .x = 0.0, .y = 0.0 })) {
|
||||
std.mem.set(u8, &state.quality_buf, 0);
|
||||
std.mem.copy(u8, &state.quality_buf, quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
igu.sliceText("Play Channel ");
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igInputText(
|
||||
"##play_channel_input",
|
||||
&state.channel_name_buf,
|
||||
state.channel_name_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igButton("Play!", .{ .x = 0.0, .y = 0.0 })) {
|
||||
start = .channel_bar;
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
igu.sliceText("Play Channel");
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
if (c.igInputText(
|
||||
"##play_channel_input",
|
||||
&state.channel_name_buf,
|
||||
state.channel_name_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igButton("Play!", .{ .x = 0.0, .y = 0.0 })) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.channels != null) {
|
||||
|
@ -104,8 +119,13 @@ pub fn winContent(state: *State) !void {
|
|||
(try std.Thread.spawn(.{}, @import("live.zig").reloadLiveThread, .{state}))
|
||||
.detach();
|
||||
}
|
||||
|
||||
c.igSameLine(0, 5.0);
|
||||
}
|
||||
|
||||
// Chatty checkbox
|
||||
_ = c.igCheckbox("Start Chatty", &state.chatty);
|
||||
|
||||
if (state.channels != null and c.igBeginChild_Str(
|
||||
"Quick Pick",
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
|
@ -133,52 +153,91 @@ pub fn winContent(state: *State) !void {
|
|||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igTableHeader("Live?");
|
||||
|
||||
for (state.channels.?) |ch, i| {
|
||||
var ch_buf: [256]u8 = undefined;
|
||||
const formatted = try std.fmt.bufPrintZ(
|
||||
&ch_buf,
|
||||
"{s}",
|
||||
.{ch.name},
|
||||
);
|
||||
|
||||
for (state.channels.?) |entry, i| {
|
||||
c.igPushID_Int(@intCast(c_int, i));
|
||||
defer c.igPopID();
|
||||
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
|
||||
if (c.igSelectable_Bool(
|
||||
formatted.ptr,
|
||||
false,
|
||||
c.ImGuiSelectableFlags_SpanAllColumns,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
)) {
|
||||
start = .{ .channels_idx = i };
|
||||
switch (entry) {
|
||||
.channel => |ch| {
|
||||
var ch_buf: [256]u8 = undefined;
|
||||
const formatted = try std.fmt.bufPrintZ(
|
||||
&ch_buf,
|
||||
"{s}",
|
||||
.{ch.name},
|
||||
);
|
||||
|
||||
if (c.igSelectable_Bool(
|
||||
formatted.ptr,
|
||||
false,
|
||||
c.ImGuiSelectableFlags_SpanAllColumns,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
)) {
|
||||
start = .{ .channels_idx = i };
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
|
||||
if (ch.comment) |comment| {
|
||||
igu.sliceText(comment);
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
|
||||
const live_color = switch (ch.live) {
|
||||
.loading => c.ImVec4{ .x = 1.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.live => c.ImVec4{ .x = 0.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.offline => c.ImVec4{ .x = 1.0, .y = 0.0, .z = 0.0, .w = 1.0 },
|
||||
};
|
||||
const live_label = switch (ch.live) {
|
||||
.loading => "Loading...",
|
||||
.live => "Live",
|
||||
.offline => "Offline",
|
||||
};
|
||||
|
||||
const prev_col = c.igGetStyle().*.Colors[c.ImGuiCol_Text];
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = live_color;
|
||||
igu.sliceText(live_label);
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = prev_col;
|
||||
},
|
||||
.separator => |heading| {
|
||||
if (heading) |h| {
|
||||
const spacer_size = c.ImVec2{ .x = 0.0, .y = 2.0 };
|
||||
|
||||
c.igDummy(spacer_size);
|
||||
const prev_col = c.igGetStyle().*.Colors[c.ImGuiCol_Text];
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = c.ImVec4{
|
||||
.x = 0.7,
|
||||
.y = 0.2,
|
||||
.z = 0.9,
|
||||
.w = 1.0,
|
||||
};
|
||||
igu.sliceText(h);
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = prev_col;
|
||||
|
||||
// TODO: is this the best way to do the alignment?
|
||||
c.igSeparator();
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
c.igDummy(spacer_size);
|
||||
c.igDummy(c.ImVec2{ .x = 0.0, .y = c.igGetTextLineHeight() });
|
||||
c.igSeparator();
|
||||
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igDummy(spacer_size);
|
||||
c.igDummy(c.ImVec2{ .x = 0.0, .y = c.igGetTextLineHeight() });
|
||||
c.igSeparator();
|
||||
} else {
|
||||
c.igSeparator();
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
c.igSeparator();
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igSeparator();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
|
||||
if (ch.comment) |comment| {
|
||||
igu.sliceText(comment);
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
|
||||
const live_color = switch (ch.live) {
|
||||
.loading => c.ImVec4{ .x = 1.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.live => c.ImVec4{ .x = 0.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.offline => c.ImVec4{ .x = 1.0, .y = 0.0, .z = 0.0, .w = 1.0 },
|
||||
};
|
||||
const live_label = switch (ch.live) {
|
||||
.loading => "Loading...",
|
||||
.live => "Live",
|
||||
.offline => "Offline",
|
||||
};
|
||||
|
||||
const prev_col = c.igGetStyle().*.Colors[c.ImGuiCol_Text];
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = live_color;
|
||||
igu.sliceText(live_label);
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = prev_col;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +286,7 @@ pub fn winContent(state: *State) !void {
|
|||
},
|
||||
.channels_idx => |idx| {
|
||||
c.glfwHideWindow(state.win);
|
||||
try launch.launchChildren(state, state.channels.?[idx].name);
|
||||
try launch.launchChildren(state, state.channels.?[idx].channel.name);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ pub fn reloadLiveThread(s: *State) !void {
|
|||
defer s.mutex.unlock();
|
||||
|
||||
for (s.channels.?) |*chan| {
|
||||
chan.live = .loading;
|
||||
switch (chan.*) {
|
||||
.channel => |*ch| ch.live = .loading,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +48,9 @@ pub fn fetchChannelsLive(s: *State) !void {
|
|||
// we shouldn't need to aquire the mutex here, this data isnt being read and we're
|
||||
// only doing atomic writes.
|
||||
var fmt_buf: [512]u8 = undefined;
|
||||
for (s.channels.?) |*chan| {
|
||||
for (s.channels.?) |*entry| {
|
||||
const chan = if (entry.* == .channel) &entry.channel else continue;
|
||||
|
||||
page_buf.clearRetainingCapacity();
|
||||
|
||||
log.info("requesting live state for channel {s}", .{chan.name});
|
||||
|
|
Loading…
Reference in a new issue