Lots of stuff

This commit is contained in:
Mathew Velasquez 2016-12-06 23:49:17 -05:00
parent cce13d8d01
commit aa7818847c
14 changed files with 531 additions and 47 deletions

View File

@ -5,50 +5,74 @@
#include <SDL.h>
#define OUT_BUFFER_SIZE 256
#if defined __linux
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>
#endif
#define BUFFER_SIZE 256
static SDL_Thread *thread = NULL;
static SDL_mutex *mutex = NULL;
static volatile char message_buffer[OUT_BUFFER_SIZE];
static volatile int message_len = 0;
static volatile char message_buffer[BUFFER_SIZE];
static volatile bool active = false;
#if defined __linux
#define PIPE_PATH "/tmp/oneshot-pipe"
static volatile int message_len = 0;
static volatile int out_pipe = -1;
void cleanup_pipe()
{
unlink(PIPE_PATH);
}
#endif
int server_thread(void *data)
{
(void)data;
#if defined _WIN32
HANDLE pipe = CreateNamedPipeW(L"\\\\.\\pipe\\oneshot-journal-to-game",
PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
OUT_BUFFER_SIZE,
OUT_BUFFER_SIZE,
BUFFER_SIZE,
BUFFER_SIZE,
0,
NULL);
for (;;) {
ConnectNamedPipe(pipe, NULL);
SDL_LockMutex(mutex);
WriteFile(pipe, (const void*)message_buffer, OUT_BUFFER_SIZE, NULL, NULL);
DWORD written;
WriteFile(pipe, (const void*)message_buffer, BUFFER_SIZE, &written, NULL);
active = true;
SDL_UnlockMutex(mutex);
FlushFileBuffers(pipe);
DisconnectNamedPipe(pipe);
}
CloseHandle(pipe);
#elif defined __linux
out_pipe = open(PIPE_PATH, O_WRONLY);
active = true;
SDL_LockMutex(mutex);
if (message_len > 0)
write(out_pipe, (char*)message_buffer, message_len);
SDL_UnlockMutex(mutex);
return 0;
#endif
}
RB_METHOD(journalSet)
{
RB_UNUSED_PARAM;
const char *name;
int len;
rb_get_args(argc, argv, "s", &name, &len RB_ARG_END);
#ifdef _WIN32
if (thread == NULL) {
thread = SDL_CreateThread(server_thread, "journal", NULL);
}
rb_get_args(argc, argv, "z", &name RB_ARG_END);
#if defined _WIN32
SDL_LockMutex(mutex);
strcpy((char*)message_buffer, name);
SDL_UnlockMutex(mutex);
HANDLE pipe = CreateFileW(L"\\\\.\\pipe\\oneshot-game-to-journal",
GENERIC_WRITE,
0,
@ -56,28 +80,60 @@ RB_METHOD(journalSet)
OPEN_EXISTING,
0,
NULL);
SDL_LockMutex(mutex);
strcpy((char*)message_buffer, name);
message_len = len;
SDL_UnlockMutex(mutex);
if (pipe != INVALID_HANDLE_VALUE) {
active = true;
WriteFile(pipe, (const void*)message_buffer, OUT_BUFFER_SIZE, NULL, NULL);
DWORD written;
WriteFile(pipe, (const void*)message_buffer, BUFFER_SIZE, &written, NULL);
FlushFileBuffers(pipe);
CloseHandle(pipe);
}
if (thread == NULL) {
thread = SDL_CreateThread(server_thread, "journal", NULL);
}
#elif defined __linux
// Clean up connection thread
if (thread != NULL && out_pipe != -1) {
SDL_WaitThread(thread, NULL);
thread = NULL;
}
// Record message
SDL_LockMutex(mutex);
message_len = strlen(name);
memcpy((char*)message_buffer, name, message_len);
SDL_UnlockMutex(mutex);
// Attempt to send it over the tubes
if (out_pipe != -1) {
// We have a connection, so send it over
if (write(out_pipe, (char*)message_buffer, message_len) <= 0) {
// In the case of an error, close
close(out_pipe);
out_pipe = -1;
}
}
if (out_pipe == -1) {
// We don't have a pipe open, so spawn the connection thread
thread = SDL_CreateThread(server_thread, "journal", NULL);
}
#else
#error "not yet implemented"
#endif
return Qnil;
}
RB_METHOD(journalActive)
{
RB_UNUSED_PARAM;
return active ? Qtrue : Qfalse;
}
void journalBindingInit()
{
mutex = SDL_CreateMutex();
#if defined __linux
mkfifo(PIPE_PATH, 0666);
atexit(cleanup_pipe);
#endif
VALUE module = rb_define_module("Journal");
_rb_define_module_function(module, "set", journalSet);

View File

@ -4,26 +4,34 @@
#include "eventthread.h"
#include "debugwriter.h"
#ifdef _WIN32
#if defined _WIN32
#include <shlwapi.h>
#endif
#include <SDL.h>
namespace syswm {
#include <SDL_syswm.h>
}
#define NIKO_X (320 - 16)
#define NIKO_Y ((13 * 16) * 2)
RB_METHOD(nikoStart)
{
RB_UNUSED_PARAM;
// Calculate where to stick the window
POINT pos;
pos.x = 320 - 16;
pos.y = (13 * 16) * 2;
SDL_SysWMinfo syswindow;
// Prime native window info
syswm::SDL_SysWMinfo syswindow;
SDL_VERSION(&syswindow.version);
SDL_GetWindowWMInfo(shState->rtData().window, &syswindow);
#if defined _WIN32
// Calculate where to stick the window
POINT pos;
pos.x = NIKO_X;
pos.y = NIKO_Y;
ClientToScreen(syswindow.info.win.window, &pos);
// Start process
#ifdef _WIN32
WCHAR path[MAX_PATH];
WCHAR args[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);
@ -35,10 +43,36 @@ RB_METHOD(nikoStart)
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
CreateProcessW(path, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
#elif defined __linux
// Calculate where to stick the window
int x, y; // Top-left area of client (hopefully)
SDL_GetWindowPosition(shState->rtData().window, &x, &y);
x += NIKO_X;
y += NIKO_Y;
char path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", path, PATH_MAX);
if (len > 0) {
// Replace filename
for (size_t i = len; i > 0; --i) {
if (path[i-1] == '/') {
strcpy(path + i, "_______");
break;
}
}
// X and Y string arguments
char x_str[16];
char y_str[16];
sprintf(x_str, "%d", x);
sprintf(y_str, "%d", y);
// Assume that was a success and run the binary
pid_t pid = fork();
if (pid == 0) {
execl(path, "_______", x_str, y_str, NULL);
exit(1);
}
}
#else
#ifdef linux
#else
#endif
#error "not yet implemented"
#endif
return Qnil;
}

View File

@ -46,7 +46,9 @@ unix:!macx {
linux/display.h \
linux/image.h
LIBS += -lxcb -lxcb-util -lxcb-image -lxcb-shape
QMAKE_CFLAGS += -pthread
LIBS += -pthread -lxcb -lxcb-util -lxcb-image -lxcb-shape -lxcb-icccm -lxcb-ewmh
}
# OS X

View File

@ -11,5 +11,7 @@ struct Display {
};
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define STRING(x) sizeof(x) - 1, (x)
#define ARRAY(x) sizeof(x), (x)
#endif // DISPLAY_H

View File

@ -1,6 +1,235 @@
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_icccm.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include "display.h"
#include "image.h"
#define PIPE_DIR "/tmp"
#define PIPE_NAME "oneshot-pipe"
#define PIPE_PATH PIPE_DIR "/" PIPE_NAME
#define MESSAGE_BUF_SIZE 256
#define PAGES \
PAGE(default) \
PAGE(c1) \
PAGE(c2) \
PAGE(c3) \
PAGE(c4) \
PAGE(c5) \
PAGE(c6) \
PAGE(c7) \
PAGE(s1) \
PAGE(s2) \
PAGE(s3) \
PAGE(s4) \
PAGE(t1) \
PAGE(t2) \
PAGE(t3) \
PAGE(t4) \
PAGE(t5) \
PAGE(t6) \
PAGE(t7) \
PAGE(t8) \
PAGE(t9) \
PAGE(t10) \
PAGE(t11) \
PAGE(t12) \
PAGE(t13) \
PAGE(t14) \
PAGE(t15) \
PAGE(t16) \
PAGE(final) \
PAGE(save)
struct ThreadData {
struct Display *dpy;
xcb_window_t window;
xcb_gcontext_t gc;
xcb_pixmap_t *masks;
xcb_pixmap_t *images;
volatile int page;
};
void set_page(struct ThreadData *threaddata, const char *name)
{
int page = 0;
#define PAGE(x) if (strncmp(name, #x, MESSAGE_BUF_SIZE) == 0) { goto end; } else { ++page; }
PAGES
#undef PAGE
end:
threaddata->page = page;
xcb_shape_mask(threaddata->dpy->c, ShapeSet, ShapeBounding,
threaddata->window, 0, 0, threaddata->masks[page]);
xcb_copy_area(threaddata->dpy->c,
threaddata->images[page],
threaddata->window,
threaddata->gc,
0, 0, 0, 0, JOURNAL_WIDTH, JOURNAL_HEIGHT);
xcb_flush(threaddata->dpy->c);
}
static void *ipc_thread(void *data)
{
struct ThreadData *threaddata = (struct ThreadData*)data;
for (;;) {
// Try to open pipe
int pipe = open(PIPE_PATH, O_RDONLY);
if (pipe == -1) {
// Use the default image for now
set_page(threaddata, "default");
// Wait until the pipe appears to try again
int inotify = inotify_init();
inotify_add_watch(inotify, PIPE_DIR, IN_CREATE);
char inotify_buf[sizeof(struct inotify_event) + NAME_MAX + 1];
for (;;) {
ssize_t result = read(inotify, inotify_buf, sizeof(inotify_buf));
if (result > 0) {
struct inotify_event *e = (struct inotify_event*)inotify_buf;
if (strcmp(e->name, PIPE_NAME) == 0)
break;
} else {
break;
}
}
close(inotify);
} else {
// Message loop
char message[MESSAGE_BUF_SIZE];
for (;;) {
ssize_t len = read(pipe, message, sizeof(message));
if (len > 0) {
set_page(threaddata, message);
} else if (len == 0) {
exit(0); // empty string
} else {
// End of the line
break;
}
}
close(pipe);
}
}
return NULL;
}
int do_journal(struct Display *dpy)
{
// Connect EWMH
xcb_ewmh_connection_t ewmh;
xcb_ewmh_init_atoms_replies(&ewmh, xcb_ewmh_init_atoms(dpy->c, &ewmh), NULL);
// Request some atoms
xcb_intern_atom_cookie_t WM_DELETE_WINDOW_cookie = xcb_intern_atom(dpy->c, false, STRING("WM_DELETE_WINDOW"));
xcb_intern_atom_cookie_t _NET_WM_PING_cookie = xcb_intern_atom(dpy->c, false, STRING("_NET_WM_PING"));
xcb_intern_atom_cookie_t WM_PROTOCOLS_cookie = xcb_intern_atom(dpy->c, false, STRING("WM_PROTOCOLS"));
xcb_atom_t WM_DELETE_WINDOW = xcb_intern_atom_reply(dpy->c, WM_DELETE_WINDOW_cookie, NULL)->atom;
xcb_atom_t _NET_WM_PING = xcb_intern_atom_reply(dpy->c, _NET_WM_PING_cookie, NULL)->atom;
xcb_atom_t WM_PROTOCOLS = xcb_intern_atom_reply(dpy->c, WM_PROTOCOLS_cookie, NULL)->atom;
// Create window
uint32_t values[] = {
XCB_EVENT_MASK_EXPOSURE, // event mask
};
xcb_window_t window = xcb_generate_id(dpy->c);
xcb_create_window(dpy->c, XCB_COPY_FROM_PARENT, window, dpy->screen->root,
0, 0, JOURNAL_WIDTH, JOURNAL_HEIGHT,
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, dpy->screen->root_visual,
XCB_CW_EVENT_MASK, &values);
xcb_atom_t protocols[] = {
WM_DELETE_WINDOW,
_NET_WM_PING,
};
xcb_icccm_set_wm_protocols(dpy->c, window, WM_PROTOCOLS, ARRAY(protocols));
xcb_icccm_set_wm_name(dpy->c, window, XCB_ATOM_STRING, 8, STRING(""));
xcb_icccm_set_wm_class(dpy->c, window, ARRAY("_______\0OneshotJournal"));
xcb_ewmh_set_wm_window_type(&ewmh, window, 1, &ewmh._NET_WM_WINDOW_TYPE_NORMAL);
xcb_size_hints_t size_hints;
xcb_icccm_size_hints_set_min_size(&size_hints, JOURNAL_WIDTH, JOURNAL_HEIGHT);
xcb_icccm_size_hints_set_max_size(&size_hints, JOURNAL_WIDTH, JOURNAL_HEIGHT);
xcb_icccm_set_wm_size_hints(dpy->c, window, XCB_ATOM_WM_NORMAL_HINTS, &size_hints);
// Create pixmaps
xcb_pixmap_t masks[] = {
#define PAGE(x) create_mask(dpy->c, window, x##_mask, JOURNAL_WIDTH, JOURNAL_HEIGHT),
PAGES
#undef PAGE
};
xcb_pixmap_t images[] = {
#define PAGE(x) create_image(dpy->c, window, x##_rgb, JOURNAL_WIDTH, JOURNAL_HEIGHT),
PAGES
#undef PAGE
};
// Create graphics context
xcb_gcontext_t gc = xcb_generate_id(dpy->c);
xcb_create_gc(dpy->c, gc, window, 0, NULL);
// Start IPC thread
struct ThreadData threaddata = {
dpy,
window,
gc,
masks,
images,
0,
};
pthread_t thread;
if (pthread_create(&thread, NULL, ipc_thread, &threaddata) != 0)
return 1;
// Map!
xcb_map_window(dpy->c, window);
xcb_flush(dpy->c);
// Handle events
xcb_generic_event_t *e;
while ((e = xcb_wait_for_event(dpy->c))) {
switch (e->response_type & ~0x80) {
case XCB_EXPOSE: {
xcb_expose_event_t *ee = (xcb_expose_event_t*)e;
xcb_copy_area(dpy->c, images[threaddata.page],
window, gc,
ee->x, ee->y, ee->x, ee->y,
ee->width, ee->height);
xcb_flush(dpy->c);
} break;
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *ce = (xcb_client_message_event_t*)e;
if (ce->type == WM_PROTOCOLS && ce->format == 32) {
xcb_atom_t atom = ce->data.data32[0];
if (atom == WM_DELETE_WINDOW) {
goto end;
} else if (atom == _NET_WM_PING) {
ce->window = dpy->screen->root;
xcb_send_event(dpy->c, false, dpy->screen->root,
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
(const char*)e);
}
}
} break;
}
free(e);
}
end:
xcb_free_gc(dpy->c, gc);
for (size_t i = 0; i < ARRAY_SIZE(masks); ++i) {
xcb_free_pixmap(dpy->c, masks[i]);
xcb_free_pixmap(dpy->c, images[i]);
}
return 0;
}

View File

@ -86,6 +86,7 @@ int do_niko(struct Display *dpy, int x, int y)
xcb_flush(dpy->c);
} break;
}
free(e);
} else if (getticks() - ticks >= 1000 / 60) {
break;
}

View File

@ -6,6 +6,8 @@
#define DEFAULT_HEIGHT 600
#define IN_BUFFER_SIZE 256
#define WINDOW_STYLE (WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME | WS_MAXIMIZEBOX))
static const WCHAR journal_classname[] = L"journal";
static HBITMAP image_handle = NULL;
@ -132,7 +134,7 @@ int do_journal()
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = module;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIcon = LoadIcon(NULL, "MAINICON");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
@ -142,12 +144,19 @@ int do_journal()
if (!RegisterClassEx(&wc))
return 1;
RECT winrect;
winrect.left = 0;
winrect.top = 0;
winrect.right = DEFAULT_WIDTH;
winrect.bottom = DEFAULT_HEIGHT;
AdjustWindowRectEx(&winrect, WINDOW_STYLE, FALSE, 0);
if (!(window = CreateWindowExW(WS_EX_COMPOSITED | WS_EX_LAYERED,
journal_classname,
L"",
WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME | WS_MAXIMIZEBOX),
WINDOW_STYLE,
CW_USEDEFAULT, CW_USEDEFAULT,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
winrect.right - winrect.left, winrect.bottom - winrect.top,
NULL, NULL, module, NULL))) {
return 1;
}

View File

@ -56,6 +56,7 @@ unix {
PKGCONFIG += openal zlib
INCLUDEPATH += /usr/include/AL /usr/local/include/AL
SOURCES += src/xdg-user-dir-lookup.c
LIBS += -lX11
}
}

View File

@ -106,7 +106,7 @@ bool EventThread::allocUserEvents()
EventThread::EventThread()
: fullscreen(false),
showCursor(false)
showCursor(true)
{}
void EventThread::process(RGSSThreadData &rtData)

View File

@ -65,7 +65,7 @@ void subRectImageEnd()
}
}
#define HAVE_NATIVE_VAO gl.GenVertexArrays
#define HAVE_NATIVE_VAO false //gl.GenVertexArrays
static void vaoBindRes(VAO &vao)
{

View File

@ -42,7 +42,7 @@ struct GLProperty
}
void push() { stack.push(current); }
void pop() { set(stack.top()); stack.pop(); }
void pop() { if (!stack.empty()) { set(stack.top()); stack.pop(); } }
const T &get() { return current; }
void set(const T &value)
{

View File

@ -7,8 +7,17 @@
/* Achievements */
static const char *const achievementNames[] = {
"SaveWorld",
"SaveNiko",
"CHAOTIC_EVIL",
"SHOCK",
"EXTREME_BARTENDING",
"RAM_WHISPERER",
"PANCAKES",
"WE_RIDE_AT_DAWN",
"SECRET",
"BOOKWORM",
"REBIRTH",
"ONESHOT",
"RETURN",
};
#define NUM_ACHIEVEMENTS (sizeof(achievementNames) / sizeof(achievementNames[0]))

View File

@ -29,7 +29,146 @@
#include <stdlib.h>
#include <string.h>
/**
* xdg_user_dir_lookup_with_fallback:
* @type: a string specifying the type of directory
* @fallback: value to use if the directory isn't specified by the user
* @returns: a newly allocated absolute pathname
*
* Looks up a XDG user directory of the specified type.
* Example of types are "DESKTOP" and "DOWNLOAD".
*
* In case the user hasn't specified any directory for the specified
* type the value returned is @fallback.
*
* The return value is newly allocated and must be freed with
* free(). The return value is never NULL if @fallback != NULL, unless
* out of memory.
**/
static char *
xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback)
{
FILE *file;
char *home_dir, *config_home, *config_file;
char buffer[512];
char *user_dir;
char *p, *d;
int len;
int relative;
home_dir = getenv ("HOME");
if (home_dir == NULL)
goto error;
config_home = getenv ("XDG_CONFIG_HOME");
if (config_home == NULL || config_home[0] == 0)
{
config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
if (config_file == NULL)
goto error;
strcpy (config_file, home_dir);
strcat (config_file, "/.config/user-dirs.dirs");
}
else
{
config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
if (config_file == NULL)
goto error;
strcpy (config_file, config_home);
strcat (config_file, "/user-dirs.dirs");
}
file = fopen (config_file, "r");
free (config_file);
if (file == NULL)
goto error;
user_dir = NULL;
while (fgets (buffer, sizeof (buffer), file))
{
/* Remove newline at end */
len = strlen (buffer);
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = 0;
p = buffer;
while (*p == ' ' || *p == '\t')
p++;
if (strncmp (p, "XDG_", 4) != 0)
continue;
p += 4;
if (strncmp (p, type, strlen (type)) != 0)
continue;
p += strlen (type);
if (strncmp (p, "_DIR", 4) != 0)
continue;
p += 4;
while (*p == ' ' || *p == '\t')
p++;
if (*p != '=')
continue;
p++;
while (*p == ' ' || *p == '\t')
p++;
if (*p != '"')
continue;
p++;
relative = 0;
if (strncmp (p, "$HOME/", 6) == 0)
{
p += 6;
relative = 1;
}
else if (*p != '/')
continue;
if (relative)
{
user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1);
if (user_dir == NULL)
goto error2;
strcpy (user_dir, home_dir);
strcat (user_dir, "/");
}
else
{
user_dir = (char*) malloc (strlen (p) + 1);
if (user_dir == NULL)
goto error2;
*user_dir = 0;
}
d = user_dir + strlen (user_dir);
while (*p && *p != '"')
{
if ((*p == '\\') && (*(p+1) != 0))
p++;
*d++ = *p++;
}
*d = 0;
}
error2:
fclose (file);
if (user_dir)
return user_dir;
error:
if (fallback)
return strdup (fallback);
return NULL;
}
/**
* xdg_user_dir_lookup:

View File

@ -7,7 +7,7 @@ HOST ?= linux64
FLAGS := -I$(STEAMWORKS)/public -DGAME_LAUNCH_NAME=\"$(GAME_LAUNCH_NAME)\" -Wall
ifeq ($(HOST),w32)
FLAGS += -L$(STEAMWORKS)/redistributable_bin -mwindows
FLAGS += -L$(STEAMWORKS)/redistributable_bin
else ifeq ($(HOST),linux32)
FLAGS += -L$(STEAMWORKS)/redistributable_bin/linux32 -m32
else ifeq ($(HOST),linux64)
@ -20,6 +20,8 @@ FLAGS += -lsteam_api
ifeq ($(DEBUG),1)
FLAGS += -DSTEAMSHIM_DEBUG
else
FLAGS += -mwindows
endif
SRC := steamshim_parent.cpp