Commit: 29dd100505556b43d27fd440fbee7e7201c88e67
Parent: fec440a921cd85c312ff0d1dfded51e45c4e6320
Author: Randy Palamar
Date: Thu, 7 Nov 2024 08:09:28 -0700
platform_x11: support primary selection
Diffstat:
4 files changed, 84 insertions(+), 7 deletions(-)
diff --git a/platform_linux_x11.c b/platform_linux_x11.c
@@ -2,6 +2,16 @@
#define GL_GLEXT_PROTOTYPES 1
#include <GLFW/glfw3.h>
+/* TODO: fix glfw */
+typedef void *RROutput;
+typedef void *RRCrtc;
+typedef void *Display;
+typedef void *Window;
+
+#define GLFW_EXPOSE_NATIVE_X11
+#define GLFW_NATIVE_INCLUDE_NONE
+#include <GLFW/glfw3native.h>
+
#include "util.h"
#include "vtgl.h"
@@ -22,11 +32,12 @@ static void *libhandle;
/* TODO: cleanup */
typedef void reset_terminal_fn(TerminalMemory *);
-#define LIB_FNS \
- X(debug_begin_frame) \
- X(debug_end_frame) \
- X(reset_terminal) \
- X(vtgl_initialize) \
+#define LIB_FNS \
+ X(debug_begin_frame) \
+ X(debug_end_frame) \
+ X(reset_terminal) \
+ X(vtgl_active_selection) \
+ X(vtgl_initialize) \
X(vtgl_frame_step)
#define X(name) static name ## _fn *name;
@@ -295,6 +306,22 @@ update_input(GLFWwindow *win, TerminalInput *input)
input->mouse_buttons[i].transitions = 0;
}
+static PLATFORM_GET_SELECTION_FN(x11_get_selection)
+{
+ /* NOTE: this does a bunch of extra copying and other garbage. both GLFW and X11 are
+ * at fault. The API is designed to do what the terminal wants and not be constrained
+ * by GLFW and X11 garbage */
+ b32 result = 0;
+ if (buffer) {
+ char *selection = (char *)glfwGetX11SelectionString();
+ if (selection) {
+ stream_push_s8(buffer, c_str_to_s8(selection));
+ result = !buffer->errors;
+ }
+ }
+ return result;
+}
+
static void
usage(char *argv0, Stream *err)
{
@@ -325,9 +352,10 @@ main(i32 argc, char *argv[], char *envp[])
term_memory.platform_api.read = posix_read;
term_memory.platform_api.write = posix_write;
term_memory.platform_api.allocate_ring_buffer = posix_allocate_ring_buffer;
+ term_memory.platform_api.get_selection = x11_get_selection;
- Arena error_arena = os_new_arena(MEGABYTE / 4);
- Stream error_stream = arena_stream(error_arena);
+ Arena platform_arena = os_new_arena(2 * MEGABYTE);
+ Stream error_stream = stream_alloc(&platform_arena, MEGABYTE / 4);
iv2 cells = {.x = -1, .y = -1};
@@ -418,6 +446,7 @@ main(i32 argc, char *argv[], char *envp[])
reset_terminal(&term_memory);
}
+ Range last_sel = {0};
f64 last_time = os_get_time();
while (!glfwWindowShouldClose(window)) {
update_input(window, &input);
@@ -445,6 +474,16 @@ main(i32 argc, char *argv[], char *envp[])
vtgl_frame_step(&term_memory, &input);
+ Range current_sel = vtgl_active_selection(&term_memory, 0);
+ if (is_valid_range(current_sel) && !equal_range(current_sel, last_sel)) {
+ Stream buf = arena_stream(platform_arena);
+ vtgl_active_selection(&term_memory, &buf);
+ stream_push_byte(&buf, 0);
+ if (!buf.errors)
+ glfwSetX11SelectionString((c8 *)buf.buf);
+ last_sel = current_sel;
+ }
+
debug_end_frame(&term_memory);
glfwSwapBuffers(window);
diff --git a/util.c b/util.c
@@ -44,6 +44,13 @@ is_valid_range(Range r)
}
static b32
+equal_range(Range a, Range b)
+{
+ b32 result = equal_iv2(a.start, b.start) && equal_iv2(a.end, b.end);
+ return result;
+}
+
+static b32
point_in_rect(v2 point, Rect rect)
{
v2 max = {.x = rect.pos.x + rect.size.w, .y = rect.pos.y + rect.size.h};
diff --git a/vtgl.c b/vtgl.c
@@ -30,6 +30,14 @@ clear_colour(void)
glClear(GL_COLOR_BUFFER_BIT);
}
+/* TODO: move this elsewhere */
+static b32
+pressed_this_frame(ButtonState *button)
+{
+ b32 result = button->ended_down && button->transitions;
+ return result;
+}
+
static void
set_projection_matrix(GLCtx *gl)
{
@@ -754,6 +762,12 @@ handle_keybindings(Term *t, TerminalInput *input, PlatformAPI *platform)
scroll(t, a);
}
}
+
+ if (pressed_this_frame(input->mouse_buttons + MOUSE_MIDDLE)) {
+ Stream buffer = arena_stream(t->arena_for_frame);
+ if (platform->get_selection(&buffer))
+ platform->write(t->child, stream_to_s8(&buffer), 0);
+ }
}
static void
@@ -913,6 +927,14 @@ DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
return requested_size;
}
+DEBUG_EXPORT VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection)
+{
+ Term *t = memory->memory;
+ Range result = t->selection.range;
+ if (out) stream_push_selection(out, t->views + t->view_idx, t->selection.range, t->size.w);
+ return result;
+}
+
DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step)
{
BEGIN_TIMED_BLOCK();
diff --git a/vtgl.h b/vtgl.h
@@ -11,10 +11,16 @@ typedef PLATFORM_WRITE_FN(platform_write_fn);
#define PLATFORM_READ_FN(name) size name(iptr file, s8 buffer, size offset)
typedef PLATFORM_READ_FN(platform_read_fn);
+#define PLATFORM_GET_SELECTION_FN(name) b32 name(Stream *buffer)
+typedef PLATFORM_GET_SELECTION_FN(platform_get_selection_fn);
+PLATFORM_GET_SELECTION_FN(get_selection_stub) {return 0;};
+
typedef struct {
platform_allocate_ring_buffer_fn *allocate_ring_buffer;
platform_read_fn *read;
platform_write_fn *write;
+
+ platform_get_selection_fn *get_selection;
} PlatformAPI;
enum mouse_buttons {
@@ -71,4 +77,7 @@ typedef VTGL_INITIALIZE_FN(vtgl_initialize_fn);
#define VTGL_FRAME_STEP_FN(name) void name(TerminalMemory *memory, TerminalInput *input)
typedef VTGL_FRAME_STEP_FN(vtgl_frame_step_fn);
+#define VTGL_ACTIVE_SELECTION_FN(name) Range name(TerminalMemory *memory, Stream *out)
+typedef VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection_fn);
+
#endif /*_VTGL_H_ */