More linux platform layer funcs

master
chr 2020-01-11 21:27:40 -08:00
parent 55a5765e03
commit 8f6daa1c28
1 changed files with 467 additions and 10 deletions

View File

@ -48,10 +48,95 @@
#include "4ed_search_list.cpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#define Cursor XCursor
#undef function
#include <X11/Xlib.h>
#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){