summaryrefslogtreecommitdiff
path: root/archive/Podium/p_platform_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'archive/Podium/p_platform_linux.c')
-rw-r--r--archive/Podium/p_platform_linux.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/archive/Podium/p_platform_linux.c b/archive/Podium/p_platform_linux.c
new file mode 100644
index 0000000..366e3d7
--- /dev/null
+++ b/archive/Podium/p_platform_linux.c
@@ -0,0 +1,404 @@
+/* linux */
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+/* x11 */
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+/* pulse audio */
+#include <pulse/pulseaudio.h>
+#include <pulse/simple.h>
+#include <pulse/error.h>
+/* joystick */
+#include <linux/joystick.h>
+
+typedef struct {
+ pa_simple *audio_stream;
+} P_LinuxCtx;
+
+struct P_Window_Impl {
+ Display *display;
+ XImage *image;
+ Window window;
+ GC gc;
+ int screen;
+ int width;
+ int height;
+};
+
+P_LinuxCtx p_ctx = {0};
+
+static inline P_KeyCode
+_x11_translate_keycode(XEvent xev)
+{
+ static KeySym ks;
+ ks = XLookupKeysym(&xev.xkey, 0);
+ switch (ks) {
+ /* Arrow keys */
+ case XK_Left: return P_KEY_LEFT;
+ case XK_Right: return P_KEY_RIGHT;
+ case XK_Up: return P_KEY_UP;
+ case XK_Down: return P_KEY_DOWN;
+ /* Common keys */
+ case XK_space: return P_KEY_SPACE;
+ case XK_Escape: return P_KEY_ESCAPE;
+ case XK_Return: return P_KEY_ENTER;
+ case XK_A: case XK_a: return P_KEY_A;
+ case XK_B: case XK_b: return P_KEY_B;
+ case XK_C: case XK_c: return P_KEY_C;
+ case XK_D: case XK_d: return P_KEY_D;
+ case XK_E: case XK_e: return P_KEY_E;
+ case XK_F: case XK_f: return P_KEY_F;
+ case XK_G: case XK_g: return P_KEY_G;
+ case XK_H: case XK_h: return P_KEY_H;
+ case XK_I: case XK_i: return P_KEY_I;
+ case XK_J: case XK_j: return P_KEY_J;
+ case XK_K: case XK_k: return P_KEY_K;
+ case XK_L: case XK_l: return P_KEY_L;
+ case XK_M: case XK_m: return P_KEY_M;
+ case XK_N: case XK_n: return P_KEY_N;
+ case XK_O: case XK_o: return P_KEY_O;
+ case XK_P: case XK_p: return P_KEY_P;
+ case XK_Q: case XK_q: return P_KEY_Q;
+ case XK_R: case XK_r: return P_KEY_R;
+ case XK_S: case XK_s: return P_KEY_S;
+ case XK_T: case XK_t: return P_KEY_T;
+ case XK_U: case XK_u: return P_KEY_U;
+ case XK_V: case XK_v: return P_KEY_V;
+ case XK_W: case XK_w: return P_KEY_W;
+ case XK_X: case XK_x: return P_KEY_X;
+ case XK_Y: case XK_y: return P_KEY_Y;
+ case XK_Z: case XK_z: return P_KEY_Z;
+ default: return P_KEY_UNKNOWN;
+ }
+}
+
+POAPI u64
+p_get_time(void)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec;
+}
+
+POAPI void
+p_sleep_ns(i64 ns)
+{
+ struct timespec ts;
+ ts.tv_sec = ns / 1000000000L;
+ ts.tv_nsec = ns % 1000000000L;
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+}
+
+POAPI void
+p_stdout(void *msg, usize bytes)
+{
+ write(1, msg, bytes);
+}
+
+POAPI bool
+p_file_exists(const char *path)
+{
+ return (access(path, F_OK) == 0);
+}
+
+POAPI usize
+p_file_size(const char *path)
+{
+ struct stat st;
+ if (stat(path, &st) == 0) {
+ return st.st_size;
+ }
+ return 0;
+}
+
+PODEF void
+p_file_load(const char *path, void *buf_ptr, unsigned long buf_size)
+{
+ int fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ read(fd, buf_ptr, (size_t)buf_size);
+ close(fd);
+ }
+}
+
+PODEF void
+p_file_write(const char *path, void *buf_ptr, unsigned long buf_size)
+{
+ int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644);
+ if (fd >= 0) {
+ write(fd, buf_ptr, (size_t)buf_size);
+ close(fd);
+ }
+}
+
+POAPI bool
+p_window_open(P_Window *win, int width, int height, const char *title)
+{
+ win->width = width;
+ win->height = height;
+
+ XInitThreads();
+
+ win->display = XOpenDisplay(NULL);
+ if (win->display == NULL) {
+ PERROR("Cannot open display!");
+ return false;
+ }
+
+ int screen = DefaultScreen(win->display);
+ win->window = XCreateSimpleWindow(
+ win->display,
+ RootWindow(win->display, screen),
+ 0, 0, width, height,
+ 1,
+ BlackPixel(win->display, screen),
+ BlackPixel(win->display, screen)
+ );
+
+ win->screen = screen;
+
+ XStoreName(win->display, win->window, title);
+ XSelectInput(win->display, win->window,
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | StructureNotifyMask);
+ XMapWindow(win->display, win->window);
+
+ win->gc = DefaultGC(win->display, screen);
+
+ Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(win->display, win->window, &wm_delete, 1);
+
+ return true;
+}
+
+POAPI void
+p_window_close(P_Window *win)
+{
+ if (!win) return;
+
+ if (win->image) {
+ // XDestroyImage(win->image);
+ win->image = NULL;
+ }
+
+ if (win->display) {
+ Display *dpy = win->display;
+ Window w = win->window;
+ win->display = NULL;
+ win->window = 0;
+
+ if (w) {
+ XDestroyWindow(dpy, w);
+ }
+ XSync(dpy, False);
+ }
+}
+
+POAPI bool
+p_window_is_open(P_Window *win)
+{
+ return win && win->display != NULL;
+}
+
+POAPI void
+p_window_size(P_Window *win, int *window_width, int *window_height)
+{
+ if (!win) return;
+
+ XWindowAttributes attr;
+ XGetWindowAttributes(win->display, win->window, &attr);
+ *window_width = attr.width;
+ *window_height = attr.height;
+
+ win->width = attr.width;
+ win->height = attr.height;
+}
+
+POAPI void
+p_window_draw(P_Window *win, u32 *pixels, int width, int height)
+{
+ int screen = win->screen;
+
+ win->image = XCreateImage(
+ win->display,
+ DefaultVisual(win->display, screen),
+ DefaultDepth(win->display, screen),
+ ZPixmap,
+ 0,
+ (char*)pixels,
+ width,
+ height,
+ 32,
+ width * 4
+ );
+
+ int window_width, window_height;
+ p_window_size(win, &window_width, &window_height);
+
+ XPutImage(
+ win->display,
+ win->window,
+ win->gc,
+ win->image,
+ 0, 0,
+ 0, 0,
+ win->width,
+ win->height);
+
+ XFlush(win->display);
+}
+
+POAPI bool
+p_window_poll_event(P_Window *win, P_Event *ev)
+{
+ if (XPending(win->display)) {
+ static XEvent xev;
+ XNextEvent(win->display, &xev);
+
+ switch (xev.type) {
+
+ case KeyPress:
+ ev->type = P_EVENT_KEY_DOWN;
+ ev->key.key = _x11_translate_keycode(xev);
+ return true;
+
+ case KeyRelease:
+ ev->type = P_EVENT_KEY_UP;
+ ev->key.key = _x11_translate_keycode(xev);
+ return true;
+
+ case ConfigureNotify:
+ ev->type = P_EVENT_WINDOW_RESIZE;
+ ev->resize.width = xev.xconfigure.width;
+ ev->resize.height = xev.xconfigure.height;
+ return true;
+
+ case ClientMessage:
+ if ((Atom)xev.xclient.data.l[0] ==
+ XInternAtom(win->display, "WM_DELETE_WINDOW", False)) {
+ ev->type = P_EVENT_WINDOW_CLOSE;
+ return true;
+ }
+ break;
+
+ case MotionNotify:
+ ev->type = P_EVENT_POINTER;
+ ev->pointer.state = P_POINTER_MOVED;
+ ev->pointer.button = 0;
+ ev->pointer.x = xev.xmotion.x;
+ ev->pointer.y = xev.xmotion.y;
+ return true;
+
+ case ButtonPress:
+ ev->type = P_EVENT_POINTER;
+ ev->pointer.state = P_POINTER_PRESSED;
+ ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */
+ ev->pointer.x = xev.xbutton.x;
+ ev->pointer.y = xev.xbutton.y;
+ return true;
+
+ case ButtonRelease:
+ ev->type = P_EVENT_POINTER;
+ ev->pointer.state = P_POINTER_RELEASED;
+ ev->pointer.button = xev.xbutton.button;
+ ev->pointer.x = xev.xbutton.x;
+ ev->pointer.y = xev.xbutton.y;
+ return true;
+ }
+ }
+
+ ev->type = P_EVENT_NONE;
+ return false;
+}
+
+POAPI void
+p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc)
+{
+ pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = sample_rate,
+ .channels = channels,
+ };
+
+ int error;
+ p_ctx.audio_stream = pa_simple_new(
+ NULL,
+ name,
+ PA_STREAM_PLAYBACK,
+ NULL,
+ desc,
+ &ss,
+ NULL,
+ NULL,
+ &error
+ );
+
+ if (!p_ctx.audio_stream) {
+ PERROR("PulseAudio init failed: %s\n", pa_strerror(error));
+ exit(1);
+ }
+}
+
+POAPI void
+p_audio_quit()
+{
+ if (p_ctx.audio_stream) {
+ pa_simple_free(p_ctx.audio_stream);
+ p_ctx.audio_stream = NULL;
+ }
+}
+
+POAPI void
+p_audio_write(const i16 *samples, usize count)
+{
+ if (!p_ctx.audio_stream) return;
+
+ int error;
+ if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) {
+ PERROR("PulseAudio write failed: %s\n", pa_strerror(error));
+ }
+
+ pa_simple_drain(p_ctx.audio_stream, &error);
+}
+
+#ifdef P_MODULE_VULKAN
+
+#ifndef VK_USE_PLATFORM_XLIB_KHR
+#define VK_USE_PLATFORM_XLIB_KHR
+#endif
+#include <vulkan/vulkan_xlib.h>
+
+POAPI char**
+p_vulkan_get_extensions()
+{
+ char **darr = NULL;
+ p_darray_push(darr, "VK_KHR_surface");
+ p_darray_push(darr, "VK_KHR_xcb_surface");
+ p_darray_push(darr, "VK_KHR_xlib_surface");
+ return darr;
+}
+
+POAPI bool
+p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface)
+{
+ if (!instance || !window || !window->display || !window->window || !out_surface) {
+ return false;
+ }
+
+ VkXlibSurfaceCreateInfoKHR create_info = {0};
+ create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
+ create_info.pNext = NULL;
+ create_info.flags = 0;
+ create_info.dpy = window->display;
+ create_info.window = window->window;
+
+ VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface);
+
+ return (result == VK_SUCCESS);
+}
+#endif