summaryrefslogtreecommitdiff
path: root/examples/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/ui.c')
-rwxr-xr-xexamples/ui.c420
1 files changed, 316 insertions, 104 deletions
diff --git a/examples/ui.c b/examples/ui.c
index cd355c5..810c9d6 100755
--- a/examples/ui.c
+++ b/examples/ui.c
@@ -78,9 +78,9 @@ typedef double f64;
// ================================================================
enum {
- MAX_NUM_PIXELS = 4 * 1024 * 1024,
+ MAX_NUM_PIXELS = 10 * 1024 * 1024,
MAX_INPUT_SIZE = 256,
- MAX_CLIPBOARD_SIZE = 0,
+ MAX_CLIPBOARD_SIZE = 10 * 1024 * 1024,
MAX_NUM_AUDIO_SAMPLES = 0,
MAX_NUM_SOCKETS = 0,
@@ -123,7 +123,13 @@ enum {
};
typedef struct {
- u32 key;
+ b8 ctrl : 1;
+ b8 shift : 1;
+ b8 alt : 1;
+ b8 caps : 1;
+ b8 num : 1;
+ b8 scroll : 1;
+ u16 key;
c32 c;
} Input_Key;
@@ -135,7 +141,7 @@ typedef struct {
i64 input_size;
Input_Key *input;
i64 clipboard_size;
- c8 * clipboard; // TODO
+ c8 * clipboard;
b8 done;
b8 has_focus;
b8 has_cursor;
@@ -160,8 +166,9 @@ typedef struct {
typedef i64 (*Thread_Proc)(void *user_data);
// UTF-8
-c32 read_utf8(i64 len, c8 *s);
-i32 write_utf8(c32 c, c8 *buffer);
+i32 utf8_size(c32 c);
+c32 utf8_read(i64 len, c8 *s);
+i32 utf8_write(c32 c, c8 *buffer);
// Window
void p_init(void);
@@ -170,6 +177,9 @@ i32 p_handle_events(void);
i32 p_wait_events(void);
void p_render_frame(void);
+// Clipboard
+void p_clipboard_write(i64 size, c8 *data);
+
// Sound
void p_handle_audio(i64 time_elapsed);
void p_queue_sound(i64 delay, i64 num_samples, f32 *samples);
@@ -228,19 +238,12 @@ u64 char_column_convolved(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X)
return 0;
- i64 offset = char_column_offset(c, column_index);
u64 column = 0;
+ i64 offset = char_column_offset(c, column_index);
- for (i64 y = 0; y < CHAR_NUM_BITS_Y; ++y) {
- if (!char_bit(offset, y))
- continue;
-
- u64 mask = 1ull << y;
- column |= mask;
-
- // colvolution
- if (mask != 1) column |= mask >> 1;
- }
+ for (i64 y = 0; y < CHAR_NUM_BITS_Y; ++y)
+ if (char_bit(offset, y))
+ column |= 3ull << y;
return column;
}
@@ -587,72 +590,111 @@ i32 main(i32 argc, c8 **argv) {
color = 0xffffff;
for (i64 i = 0; i < platform.input_size; ++i)
- switch (platform.input[i].key) {
- case KEY_LEFT:
- if (platform.key_down[MOD_SHIFT]) {
- if (cursor > 0)
- ++selection;
- } else if (selection != 0) {
- cursor = selection < 0 ? cursor + selection + 1 : cursor + 1;
- selection = 0;
- }
- if (cursor > 0)
- --cursor;
- break;
+ if (platform.input[i].ctrl)
+ switch (platform.input[i].key) {
+ case 'v': {
+ if (selection != 0) {
+ i64 i0 = selection < 0 ? cursor + selection : cursor;
+ i64 i1 = selection < 0 ? cursor : cursor + selection;
+ for (i64 i = 0; i1 + i < text_len; ++i)
+ text[i0 + i] = text[i1 + i];
+ selection = 0;
+ cursor = i0;
+ text_len -= i1 - i0;
+ }
- case KEY_RIGHT:
- if (platform.key_down[MOD_SHIFT]) {
- if (cursor < text_len)
- --selection;
- } else if (selection != 0) {
- cursor = selection < 0 ? cursor - 1 : cursor + selection - 1;
- selection = 0;
- }
- if (cursor < text_len)
- ++cursor;
- break;
+ for (i64 n = 0; n < platform.clipboard_size;) {
+ c32 c = utf8_read(platform.clipboard_size - n, platform.clipboard + n);
+
+ if (text_len < (i64) (sizeof text / sizeof *text)) {
+ for (i64 j = text_len; j > cursor; --j)
+ text[j] = text[j - 1];
+ text[cursor++] = c;
+ ++text_len;
+ }
+
+ n += utf8_size(c);
+ }
+ } break;
+
+ case 'x': {
+ i64 len = 0;
+ static c8 buf[1024];
- case '\b':
- if (selection != 0) {
i64 i0 = selection < 0 ? cursor + selection : cursor;
i64 i1 = selection < 0 ? cursor : cursor + selection;
+ for (i64 i = 0; len + 4 <= (i64) sizeof buf && i < i1 - i0; ++i)
+ len += utf8_write(text[i0 + i], buf + len);
+
+ if (len > 0)
+ p_clipboard_write(len, buf);
+
for (i64 i = 0; i1 + i < text_len; ++i)
text[i0 + i] = text[i1 + i];
selection = 0;
cursor = i0;
text_len -= i1 - i0;
- } else if (cursor > 0 && text_len > 0) {
- for (i64 i = cursor; i < text_len; ++i)
- text[i - 1] = text[i];
- --cursor;
- --text_len;
- }
- break;
+ } break;
+
+ case 'c': {
+ i64 len = 0;
+ static c8 buf[1024];
- case KEY_DELETE:
- if (selection != 0) {
i64 i0 = selection < 0 ? cursor + selection : cursor;
i64 i1 = selection < 0 ? cursor : cursor + selection;
- for (i64 i = 0; i1 + i < text_len; ++i)
- text[i0 + i] = text[i1 + i];
- selection = 0;
- cursor = i0;
- text_len -= i1 - i0;
- } else if (cursor < text_len) {
- for (i64 i = cursor + 1; i < text_len; ++i)
- text[i - 1] = text[i];
- --text_len;
- }
- break;
+ for (i64 i = 0; len + 4 <= (i64) sizeof buf && i < i1 - i0; ++i)
+ len += utf8_write(text[i0 + i], buf + len);
+
+ if (len > 0)
+ p_clipboard_write(len, buf);
+ } break;
- case '\n':
- case '\r':
- case '\t':
- platform.input[i].c = platform.input[i].key;
- // fallthrough
+ default:;
+ }
+ else
+ switch (platform.input[i].key) {
+ case KEY_LEFT:
+ if (platform.key_down[MOD_SHIFT]) {
+ if (cursor > 0)
+ ++selection;
+ } else if (selection != 0) {
+ cursor = selection < 0 ? cursor + selection + 1 : cursor + 1;
+ selection = 0;
+ }
+ if (cursor > 0)
+ --cursor;
+ break;
- default:
- if (platform.input[i].c) {
+ case KEY_RIGHT:
+ if (platform.key_down[MOD_SHIFT]) {
+ if (cursor < text_len)
+ --selection;
+ } else if (selection != 0) {
+ cursor = selection < 0 ? cursor - 1 : cursor + selection - 1;
+ selection = 0;
+ }
+ if (cursor < text_len)
+ ++cursor;
+ break;
+
+ case '\b':
+ if (selection != 0) {
+ i64 i0 = selection < 0 ? cursor + selection : cursor;
+ i64 i1 = selection < 0 ? cursor : cursor + selection;
+ for (i64 i = 0; i1 + i < text_len; ++i)
+ text[i0 + i] = text[i1 + i];
+ selection = 0;
+ cursor = i0;
+ text_len -= i1 - i0;
+ } else if (cursor > 0 && text_len > 0) {
+ for (i64 i = cursor; i < text_len; ++i)
+ text[i - 1] = text[i];
+ --cursor;
+ --text_len;
+ }
+ break;
+
+ case KEY_DELETE:
if (selection != 0) {
i64 i0 = selection < 0 ? cursor + selection : cursor;
i64 i1 = selection < 0 ? cursor : cursor + selection;
@@ -661,16 +703,39 @@ i32 main(i32 argc, c8 **argv) {
selection = 0;
cursor = i0;
text_len -= i1 - i0;
+ } else if (cursor < text_len) {
+ for (i64 i = cursor + 1; i < text_len; ++i)
+ text[i - 1] = text[i];
+ --text_len;
}
+ break;
- if (text_len < (i64) sizeof text) {
- for (i64 i = text_len; i > cursor; --i)
- text[i] = text[i - 1];
- text[cursor++] = platform.input[i].c;
- ++text_len;
+ case '\n':
+ case '\r':
+ case '\t':
+ platform.input[i].c = platform.input[i].key;
+ // fallthrough
+
+ default:
+ if (platform.input[i].c) {
+ if (selection != 0) {
+ i64 i0 = selection < 0 ? cursor + selection : cursor;
+ i64 i1 = selection < 0 ? cursor : cursor + selection;
+ for (i64 i = 0; i1 + i < text_len; ++i)
+ text[i0 + i] = text[i1 + i];
+ selection = 0;
+ cursor = i0;
+ text_len -= i1 - i0;
+ }
+
+ if (text_len < (i64) (sizeof text / sizeof *text)) {
+ for (i64 i = text_len; i > cursor; --i)
+ text[i] = text[i - 1];
+ text[cursor++] = platform.input[i].c;
+ ++text_len;
+ }
}
- }
- }
+ }
draw_text_area(0, x0 + 8, y0 - 8, w, h, 10., 10., text_len, text);
draw_text_area(color, x0, y0, w, h, 10., 10., text_len, text);
@@ -690,14 +755,10 @@ i32 main(i32 argc, c8 **argv) {
// ----------------------------------------------------------------
//
// TODO
-// - Clipboard
// - Sound
// - Sockets
// - Clipboard daemon
//
-// X11 clipboard
-// https://handmade.network/forums/articles/t/8544-implementing_copy_paste_in_x11
-//
// ALSA
// https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html
//
@@ -706,23 +767,23 @@ i32 main(i32 argc, c8 **argv) {
#include <assert.h>
#include <string.h>
-c32 read_utf8(i64 len, c8 *s) {
- if (len <= 1 &&
+c32 utf8_read(i64 len, c8 *s) {
+ if (len >= 1 &&
(s[0] & 0x80) == 0)
return s[0];
- if (len <= 2 &&
+ if (len >= 2 &&
(s[0] & 0xe0) == 0xc0 &&
(s[1] & 0xc0) == 0x80)
return (s[1] & 0x3f)
| ((s[0] & 0x1f) << 6);
- if (len <= 3 &&
+ if (len >= 3 &&
(s[0] & 0xf0) == 0xe0 &&
(s[1] & 0xc0) == 0x80 &&
(s[2] & 0xc0) == 0x80)
return (s[2] & 0x3f)
| ((s[1] & 0x3f) << 6)
| ((s[0] & 0x0f) << 12);
- if (len <= 4 &&
+ if (len >= 4 &&
(s[0] & 0xf8) == 0xf0 &&
(s[1] & 0xc0) == 0x80 &&
(s[2] & 0xc0) == 0x80 &&
@@ -734,7 +795,15 @@ c32 read_utf8(i64 len, c8 *s) {
return 0;
}
-i32 write_utf8(c32 c, c8 *buffer) {
+i32 utf8_size(c32 c) {
+ if ((c & 0x00007f) == c) return 1;
+ if ((c & 0x0007ff) == c) return 2;
+ if ((c & 0x00ffff) == c) return 3;
+ if ((c & 0x1fffff) == c) return 4;
+ return 0;
+}
+
+i32 utf8_write(c32 c, c8 *buffer) {
if ((c & 0x7f) == c) {
buffer[0] = (c8) c;
return 1;
@@ -799,19 +868,25 @@ i64 p_send(u16 slot, IP_Address address, i64 size, u8 *data) {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/Xatom.h>
#include <sched.h>
#include <time.h>
-static i16 _key_table[512] = {0};
-static u32 _buffer[MAX_NUM_PIXELS] = {0};
-static Input_Key _input[MAX_INPUT_SIZE] = {0};
-static XImage _image = {0};
-static Display * _display = NULL;
-static GC _gc = NULL;
-static XIM _im = NULL;
-static XIC _ic = NULL;
-static Window _window = 0;
-static Atom _wm_delete_window = 0;
+static i16 _key_table[512] = {0};
+static u32 _buffer[MAX_NUM_PIXELS] = {0};
+static Input_Key _input[MAX_INPUT_SIZE] = {0};
+static c8 _clipboard_buffer[MAX_CLIPBOARD_SIZE] = {0};
+static XImage _image = {0};
+static Display * _display = NULL;
+static GC _gc = NULL;
+static XIM _im = NULL;
+static XIC _ic = NULL;
+static Window _window = 0;
+static Atom _wm_delete_window = 0;
+static Atom _clipboard = 0;
+static Atom _targets = 0;
+static Atom _utf8_string = 0;
+static Atom _target = None;
void p_init(void) {
_display = XOpenDisplay(NULL);
@@ -940,10 +1015,9 @@ void p_init(void) {
_ic = XCreateIC(_im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, _window, NULL);
assert(_ic != NULL);
- XSetICFocus(_ic);
-
- platform.pixels = _buffer;
- platform.input = _input;
+ platform.pixels = _buffer;
+ platform.input = _input;
+ platform.clipboard = _clipboard_buffer;
_image = (XImage) {
.width = platform.frame_width,
@@ -963,11 +1037,14 @@ void p_init(void) {
.blue_mask = 0x0000ff,
};
-
XInitImage(&_image);
_wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", False);
+ _clipboard = XInternAtom(_display, "CLIPBOARD", False);
+ _targets = XInternAtom(_display, "TARGETS", False);
+ _utf8_string = XInternAtom(_display, "UTF8_STRING", False);
+ XSetICFocus(_ic);
XSetWMProtocols(_display, _window, &_wm_delete_window, 1);
XStoreName(_display, _window, platform.title);
@@ -991,6 +1068,8 @@ i32 p_handle_events(void) {
XEvent ev;
+ b8 requested_clipboard = 0;
+
while (XEventsQueued(_display, QueuedAlready) > 0) {
++num_events;
@@ -1029,6 +1108,10 @@ i32 p_handle_events(void) {
case Button5: --platform.wheel_dy; break;
default:;
}
+ if (!requested_clipboard) {
+ XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime);
+ requested_clipboard = 1;
+ }
break;
case ButtonRelease:
@@ -1056,14 +1139,36 @@ i32 p_handle_events(void) {
platform.key_down[MOD_SCROLL] = !!(ev.xkey.state & Mod3Mask);
if (platform.input_size < MAX_INPUT_SIZE) {
if (k < 32 || k >= 128)
- platform.input[platform.input_size++] = (Input_Key) { .key = k, .c = 0, };
+ platform.input[platform.input_size++] = (Input_Key) {
+ .ctrl = !!(ev.xkey.state & ControlMask),
+ .shift = !!(ev.xkey.state & ShiftMask),
+ .alt = !!(ev.xkey.state & Mod1Mask),
+ .caps = !!(ev.xkey.state & LockMask),
+ .num = !!(ev.xkey.state & Mod2Mask),
+ .scroll = !!(ev.xkey.state & Mod3Mask),
+ .key = k,
+ .c = 0,
+ };
else {
c8 buf[16];
i32 len = Xutf8LookupString(_ic, &ev.xkey, buf, sizeof buf - 1, NULL, NULL);
if (len > 0)
- platform.input[platform.input_size++] = (Input_Key) { .key = k, .c = read_utf8(len, buf), };
+ platform.input[platform.input_size++] = (Input_Key) {
+ .ctrl = !!(ev.xkey.state & ControlMask),
+ .shift = !!(ev.xkey.state & ShiftMask),
+ .alt = !!(ev.xkey.state & Mod1Mask),
+ .caps = !!(ev.xkey.state & LockMask),
+ .num = !!(ev.xkey.state & Mod2Mask),
+ .scroll = !!(ev.xkey.state & Mod3Mask),
+ .key = k,
+ .c = utf8_read(len, buf),
+ };
}
}
+ if (!requested_clipboard) {
+ XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime);
+ requested_clipboard = 1;
+ }
} break;
case KeyRelease: {
@@ -1080,11 +1185,108 @@ i32 p_handle_events(void) {
platform.key_down[MOD_SCROLL] = !!(ev.xkey.state & Mod3Mask);
} break;
+ case SelectionRequest:
+ if (ev.xselectionrequest.requestor != _window &&
+ ev.xselectionrequest.selection == _clipboard &&
+ XGetSelectionOwner(_display, _clipboard) == _window) {
+ if (ev.xselectionrequest.property != None) {
+ if (ev.xselectionrequest.target == _targets)
+ XChangeProperty(
+ ev.xselectionrequest.display,
+ ev.xselectionrequest.requestor,
+ ev.xselectionrequest.property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (u8 *) &_utf8_string,
+ 1
+ );
+ else if (ev.xselectionrequest.target == _utf8_string)
+ XChangeProperty(
+ ev.xselectionrequest.display,
+ ev.xselectionrequest.requestor,
+ ev.xselectionrequest.property,
+ ev.xselectionrequest.target,
+ 8,
+ PropModeReplace,
+ (u8 *) platform.clipboard,
+ platform.clipboard_size
+ );
+ }
+
+ XSendEvent(_display, ev.xselectionrequest.requestor, 0, 0, (XEvent *) &(XSelectionEvent) {
+ .type = SelectionNotify,
+ .serial = ev.xselectionrequest.serial,
+ .send_event = ev.xselectionrequest.send_event,
+ .display = ev.xselectionrequest.display,
+ .requestor = ev.xselectionrequest.requestor,
+ .selection = ev.xselectionrequest.selection,
+ .target = ev.xselectionrequest.target,
+ .property = ev.xselectionrequest.property,
+ .time = ev.xselectionrequest.time,
+ });
+ }
+ break;
+
+ case SelectionNotify:
+ if (ev.xselection.property != None) {
+ i64 len;
+ u8 *data;
+
+ XGetWindowProperty(
+ _display,
+ _window,
+ _clipboard,
+ 0,
+ MAX_CLIPBOARD_SIZE,
+ False,
+ AnyPropertyType,
+ &(Atom) {0},
+ &(int) {0},
+ (unsigned long *) &len,
+ &(unsigned long) {0},
+ &data
+ );
+
+ if (ev.xselection.target == _targets) {
+ _target = None;
+ Atom *list = (Atom *) data;
+
+ for (i64 i = 0; i < len; i++)
+ if (list[i] == XA_STRING)
+ _target = XA_STRING;
+ else if (list[i] == _utf8_string) {
+ _target = _utf8_string;
+ break;
+ }
+
+ if (_target != None)
+ XConvertSelection(_display, _clipboard, _target, _clipboard, _window, CurrentTime);
+ } else if (ev.xselection.target == _target) {
+ if (len > MAX_CLIPBOARD_SIZE)
+ len = MAX_CLIPBOARD_SIZE;
+ platform.clipboard_size = len;
+ if (len > 0)
+ memcpy(platform.clipboard, data, len);
+ }
+
+ if (data)
+ XFree(data);
+ }
+ break;
+
case EnterNotify: platform.has_cursor = 1; break;
case LeaveNotify: platform.has_cursor = 0; break;
- case FocusIn: platform.has_focus = 1; break;
case FocusOut: platform.has_focus = 0; break;
+ case FocusIn:
+ platform.has_focus = 1;
+ if (!requested_clipboard) {
+ XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime);
+ requested_clipboard = 1;
+ }
+ break;
+
case MappingNotify:
XRefreshKeyboardMapping(&ev.xmapping);
break;
@@ -1137,6 +1339,16 @@ void p_render_frame(void) {
XFlush(_display);
}
+void p_clipboard_write(i64 size, c8 *data) {
+ assert(size <= MAX_CLIPBOARD_SIZE);
+
+ XSetSelectionOwner(_display, _clipboard, _window, CurrentTime);
+
+ platform.clipboard_size = size < MAX_CLIPBOARD_SIZE ? size : MAX_CLIPBOARD_SIZE;
+ if (platform.clipboard_size > 0)
+ memcpy(platform.clipboard, data, platform.clipboard_size);
+}
+
#endif
// ================================================================