More linux platform layer funcs
parent
55a5765e03
commit
8f6daa1c28
|
@ -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){
|
||||
|
|
Loading…
Reference in New Issue