From 8f6daa1c28b01acd69c88aa24c30e1ec238aa9d1 Mon Sep 17 00:00:00 2001 From: chr Date: Sat, 11 Jan 2020 21:27:40 -0800 Subject: [PATCH] More linux platform layer funcs --- platform_linux/linux_4ed.cpp | 477 ++++++++++++++++++++++++++++++++++- 1 file changed, 467 insertions(+), 10 deletions(-) diff --git a/platform_linux/linux_4ed.cpp b/platform_linux/linux_4ed.cpp index 845807f1..d7551f4f 100644 --- a/platform_linux/linux_4ed.cpp +++ b/platform_linux/linux_4ed.cpp @@ -48,10 +48,95 @@ #include "4ed_search_list.cpp" -#include -#include -#include #include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define Cursor XCursor +#undef function +#include +#define function static +#undef Cursor + +//////////////////////////// + +struct Linux_Vars { + int epoll; + Node free_linux_objects; +}; + +enum { + LINUX_4ED_EVENT_CLI = (UINT64_C(5) << 32), +}; + +typedef i32 Linux_Object_Kind; +enum { + LinuxObjectKind_Thread = 1, +}; + +struct Linux_Object { + Linux_Object_Kind kind; + Node node; + union { + struct { + pthread_t pthread; + Thread_Function* proc; + void* ptr; + } thread; + }; +}; + + +//////////////////////////// + +global Linux_Vars linuxvars; + +//////////////////////////// + +internal Linux_Object* +linux_alloc_object(Linux_Object_Kind kind){ + Linux_Object* result = NULL; + if (linuxvars.free_linux_objects.next != &linuxvars.free_linux_objects) { + result = CastFromMember(Linux_Object, node, linuxvars.free_linux_objects.next); + } + if (result == NULL) { + i32 count = 512; + Linux_Object* objects = (Linux_Object*)system_memory_allocate( + sizeof(Linux_Object), file_name_line_number_lit_u8); + objects[0].node.prev = &linuxvars.free_linux_objects; + for (i32 i = 1; i < count; ++i) { + objects[i - 1].node.next = &objects[i].node; + objects[i].node.prev = &objects[i - 1].node; + } + objects[count - 1].node.next = &linuxvars.free_linux_objects; + linuxvars.free_linux_objects.prev = &objects[count - 1].node; + result = CastFromMember(Linux_Object, node, linuxvars.free_linux_objects.next); + } + Assert(result != 0); + dll_remove(&result->node); + block_zero_struct(result); + result->kind = kind; + return result; +} + +internal void +linux_free_object(Linux_Object *object){ + if (object->node.next != 0){ + dll_remove(&object->node); + } + dll_insert(&linuxvars.free_linux_objects, &object->node); +} internal system_get_path_sig(){ @@ -84,7 +169,6 @@ system_get_path_sig(){ return(result); } - internal system_get_canonical_sig(){ // TODO(andrew): Resolve symlinks ? @@ -119,10 +203,7 @@ linux_convert_file_attribute_flags(int mode) { } internal File_Attributes -linux_get_file_attributes(String_Const_u8 file_name) { - struct stat file_stat; - stat((const char*)file_name.str, &file_stat); - +linux_file_attributes_from_struct_stat(struct stat file_stat) { File_Attributes result = {}; result.size = file_stat.st_size; result.last_write_time = linux_u64_from_timespec(file_stat.st_mtim); @@ -153,7 +234,10 @@ system_get_file_list_sig(){ sll_queue_push(first, last, info); info->file_name = SCu8((u8*)dirent->d_name); - info->attributes = linux_get_file_attributes(info->file_name); + + struct stat file_stat; + stat((const char*)dirent->d_name, &file_stat); + info->attributes = linux_file_attributes_from_struct_stat(file_stat); } result.infos = push_array(arena, File_Info*, num_ents); @@ -168,7 +252,380 @@ system_get_file_list_sig(){ internal system_quick_file_attributes_sig(){ - return linux_get_file_attributes(file_name); + struct stat file_stat; + stat((const char*)file_name.str, &file_stat); + return linux_file_attributes_from_struct_stat(file_stat); +} + +internal +system_load_handle_sig(){ + int fd = open(file_name, O_RDONLY); + if (fd != -1) { + *(int*)out = fd; + return true; + } + return false; +} + +internal +system_load_attributes_sig(){ + struct stat file_stat; + fstat(*(int*)&handle, &file_stat); + return linux_file_attributes_from_struct_stat(file_stat); +} + +internal +system_load_file_sig(){ + int fd = *(int*)&handle; + int bytes_read = read(fd, buffer, size); + if (bytes_read == size) { + return true; + } + return false; +} + +internal +system_load_close_sig(){ + int fd = *(int*)&handle; + return close(fd) == 0; +} + +internal +system_save_file_sig(){ + File_Attributes result = {}; + int fd = open(file_name, O_WRONLY, O_CREAT); + if (fd != -1) { + int bytes_written = write(fd, data.str, data.size); + if (bytes_written != -1) { + struct stat file_stat; + fstat(fd, &file_stat); + return linux_file_attributes_from_struct_stat(file_stat); + } + } + return result; +} + +typedef void* shared_object_handle; + +internal +system_load_library_sig(){ + shared_object_handle library = dlopen((const char*)file_name.str, RTLD_LAZY); + if (library != NULL) { + *(shared_object_handle*)out = library; + return true; + } + return false; +} + +internal +system_release_library_sig(){ + return dlclose(*(shared_object_handle*)&handle) == 0; +} + +internal +system_get_proc_sig(){ + return (Void_Func*)dlsym(*(shared_object_handle*)&handle, proc_name); +} + +internal +system_now_time_sig(){ + struct timespec time; + clock_gettime(CLOCK_MONOTONIC_RAW, &time); + return linux_u64_from_timespec(time); +} + +// system_wake_up_timer_create_sig +// system_wake_up_timer_release_sig +// system_wake_up_timer_set_sig +// system_signal_step_sig + +internal +system_sleep_sig(){ + struct timespec requested; + struct timespec remaining; + u64 seconds = microseconds / Million(1); + requested.tv_sec = seconds; + requested.tv_nsec = (microseconds - seconds * Million(1)) * Thousand(1); + nanosleep(&requested, &remaining); +} + +// system_post_clipboard_sig + +internal +system_cli_call_sig() { + int pipe_fds[2]; + if (pipe(pipe_fds) == -1){ + perror("system_cli_call: pipe"); + return 0; + } + + pid_t child_pid = fork(); + if (child_pid == -1){ + perror("system_cli_call: fork"); + return 0; + } + + enum { PIPE_FD_READ, PIPE_FD_WRITE }; + + // child + if (child_pid == 0){ + close(pipe_fds[PIPE_FD_READ]); + dup2(pipe_fds[PIPE_FD_WRITE], STDOUT_FILENO); + dup2(pipe_fds[PIPE_FD_WRITE], STDERR_FILENO); + + if (chdir(path) == -1){ + perror("system_cli_call: chdir"); + exit(1); + } + + char* argv[] = { "sh", "-c", script, NULL }; + + if (execv("/bin/sh", argv) == -1){ + perror("system_cli_call: execv"); + } + exit(1); + } + else{ + close(pipe_fds[PIPE_FD_WRITE]); + + *(pid_t*)&cli_out->proc = child_pid; + *(int*)&cli_out->out_read = pipe_fds[PIPE_FD_READ]; + *(int*)&cli_out->out_write = pipe_fds[PIPE_FD_WRITE]; + + struct epoll_event e = {}; + e.events = EPOLLIN | EPOLLET; + e.data.u64 = LINUX_4ED_EVENT_CLI; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, pipe_fds[PIPE_FD_READ], &e); + } + + return(true); +} + +internal +system_cli_begin_update_sig() { + // NOTE(inso): I don't think anything needs to be done here. +} + +internal +system_cli_update_step_sig(){ + int pipe_read_fd = *(int*)&cli->out_read; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipe_read_fd, &fds); + + struct timeval tv = {}; + + size_t space_left = max; + char* ptr = dest; + + while (space_left > 0 && select(pipe_read_fd + 1, &fds, NULL, NULL, &tv) == 1){ + ssize_t num = read(pipe_read_fd, ptr, space_left); + if (num == -1){ + perror("system_cli_update_step: read"); + } else if (num == 0){ + // NOTE(inso): EOF + break; + } else { + ptr += num; + space_left -= num; + } + } + + *amount = (ptr - dest); + return((ptr - dest) > 0); +} + +internal +system_cli_end_update_sig(){ + pid_t pid = *(pid_t*)&cli->proc; + b32 close_me = false; + + int status; + if (pid && waitpid(pid, &status, WNOHANG) > 0){ + cli->exit = WEXITSTATUS(status); + + close_me = true; + close(*(int*)&cli->out_read); + close(*(int*)&cli->out_write); + + struct epoll_event e = {}; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_DEL, *(int*)&cli->out_read, &e); + } + + return(close_me); +} + +// system_open_color_picker + +internal int +linux_get_xsettings_dpi(Display* dpy, int screen) +{ + struct XSettingHeader { + u8 type; + u8 pad0; + u16 name_len; + char name[0]; + }; + + struct XSettings { + u8 byte_order; + u8 pad[3]; + u32 serial; + u32 num_settings; + }; + + enum { XSettingsTypeInt, XSettingsTypeString, XSettingsTypeColor }; + + int dpi = -1; + unsigned char* prop = NULL; + char sel_buffer[64]; + struct XSettings* xs; + const char* p; + + snprintf(sel_buffer, sizeof(sel_buffer), "_XSETTINGS_S%d", screen); + + Atom XSET_SEL = XInternAtom(dpy, sel_buffer, True); + Atom XSET_SET = XInternAtom(dpy, "_XSETTINGS_SETTINGS", True); + + if (XSET_SEL == None || XSET_SET == None){ + //LOG("XSETTINGS unavailable.\n"); + return(dpi); + } + + Window xset_win = XGetSelectionOwner(dpy, XSET_SEL); + if (xset_win == None){ + // TODO(inso): listen for the ClientMessage about it becoming available? + // there's not much point atm if DPI scaling is only done at startup + goto out; + } + + { + Atom type; + int fmt; + unsigned long pad, num; + + if (XGetWindowProperty(dpy, xset_win, XSET_SET, 0, 1024, False, XSET_SET, &type, &fmt, &num, &pad, &prop) != Success){ + //LOG("XSETTINGS: GetWindowProperty failed.\n"); + goto out; + } + + if (fmt != 8){ + //LOG("XSETTINGS: Wrong format.\n"); + goto out; + } + } + + xs = (struct XSettings*)prop; + p = (char*)(xs + 1); + + if (xs->byte_order != 0){ + //LOG("FIXME: XSETTINGS not host byte order?\n"); + goto out; + } + + for (int i = 0; i < xs->num_settings; ++i){ + struct XSettingHeader* h = (struct XSettingHeader*)p; + + p += sizeof(struct XSettingHeader); + p += h->name_len; + p += ((4 - (h->name_len & 3)) & 3); + p += 4; // serial + + switch (h->type){ + case XSettingsTypeInt: { + if (strncmp(h->name, "Xft/DPI", h->name_len) == 0){ + dpi = *(i32*)p; + if (dpi != -1) dpi /= 1024; + } + p += 4; + } break; + + case XSettingsTypeString: { + u32 len = *(u32*)p; + p += 4; + p += len; + p += ((4 - (len & 3)) & 3); + } break; + + case XSettingsTypeColor: { + p += 8; + } break; + + default: { + //LOG("XSETTINGS: Got invalid type...\n"); + goto out; + } break; + } + } + + out: + if (prop){ + XFree(prop); + } + + return dpi; +} + +// internal +// system_get_screen_scale_factor_sig(){ +// return linux_get_xsettings_dpi() / 96.0f; +// } + +internal void* +linux_thread_proc_start(void* arg) { + Linux_Object* info = (Linux_Object*)arg; + Assert(info->kind == LinuxObjectKind_Thread); + info->thread.proc(info->thread.ptr); + return NULL; +} + +internal +system_thread_launch_sig(){ + System_Thread result = {}; + Linux_Object* thread_info = linux_alloc_object(LinuxObjectKind_Thread); + thread_info->thread.proc = proc; + thread_info->thread.ptr = ptr; + pthread_attr_t thread_attr; + pthread_attr_init(&thread_attr); + int create_result = pthread_create( + &thread_info->thread.pthread, &thread_attr, linux_thread_proc_start, (void*)thread_info); + pthread_attr_destroy(&thread_attr); + // TODO(andrew): Need to wait for thread to confirm it launched? + if (create_result == 0) { + static_assert(sizeof(Linux_Object*) <= sizeof(System_Thread)); + *(Linux_Object**)&result = thread_info; + return result; + } + return result; +} + +internal +system_thread_join_sig(){ + Linux_Object* object = *(Linux_Object**)&thread; + void* retval_ignored; + int result = pthread_join(object->thread.pthread, &retval_ignored); +} + +internal +system_thread_free_sig(){ + Linux_Object* object = *(Linux_Object**)&thread; + if (object->kind == LinuxObjectKind_Thread) { + linux_free_object(object); + } +} + +internal +system_memory_allocate_sig(){ + void* result = mmap( + NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + // TODO(andrew): Allocation tracking? + return result; +} + +internal +system_memory_free_sig(){ + munmap(ptr, size); } int main(int argc, char **argv){