summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/Makefile29
-rw-r--r--desktop/grab.c51
-rw-r--r--desktop/grab.h15
-rw-r--r--desktop/icons/Faenza-Radiance/apps/16/vkpc.pngbin0 -> 512 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/22/vkpc.pngbin0 -> 685 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/24/vkpc.pngbin0 -> 621 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/32/vkpc.pngbin0 -> 904 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/48/vkpc.pngbin0 -> 1343 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/64/vkpc.pngbin0 -> 1747 bytes
-rw-r--r--desktop/icons/Faenza-Radiance/apps/96/vkpc.pngbin0 -> 2717 bytes
-rw-r--r--desktop/icons/Faenza/apps/16/vkpc.pngbin0 -> 627 bytes
-rw-r--r--desktop/icons/Faenza/apps/22/vkpc.pngbin0 -> 824 bytes
-rw-r--r--desktop/icons/Faenza/apps/24/vkpc.pngbin0 -> 804 bytes
-rw-r--r--desktop/icons/Faenza/apps/32/vkpc.pngbin0 -> 1141 bytes
-rw-r--r--desktop/icons/Faenza/apps/48/vkpc.pngbin0 -> 1685 bytes
-rw-r--r--desktop/icons/Faenza/apps/64/vkpc.pngbin0 -> 2288 bytes
-rw-r--r--desktop/icons/Faenza/apps/96/vkpc.pngbin0 -> 3596 bytes
-rw-r--r--desktop/icons/hicolor/128x128/apps/vkpc.pngbin0 -> 3663 bytes
-rw-r--r--desktop/icons/hicolor/22x22/apps/vkpc.pngbin0 -> 664 bytes
-rw-r--r--desktop/icons/hicolor/24x24/apps/vkpc.pngbin0 -> 615 bytes
-rw-r--r--desktop/icons/hicolor/32x32/apps/vkpc.pngbin0 -> 860 bytes
-rw-r--r--desktop/icons/hicolor/36x36/apps/vkpc.pngbin0 -> 999 bytes
-rw-r--r--desktop/icons/hicolor/40x40/apps/vkpc.pngbin0 -> 1119 bytes
-rw-r--r--desktop/icons/hicolor/48x48/apps/vkpc.pngbin0 -> 1302 bytes
-rw-r--r--desktop/icons/hicolor/64x64/apps/vkpc.pngbin0 -> 1917 bytes
-rw-r--r--desktop/icons/hicolor/72x72/apps/vkpc.pngbin0 -> 2184 bytes
-rw-r--r--desktop/icons/hicolor/96x96/apps/vkpc.pngbin0 -> 2825 bytes
-rw-r--r--desktop/icons/ubuntu-mono-dark/16/vkpc.pngbin0 -> 479 bytes
-rw-r--r--desktop/icons/ubuntu-mono-dark/22/vkpc.pngbin0 -> 559 bytes
-rw-r--r--desktop/icons/ubuntu-mono-dark/24/vkpc.pngbin0 -> 597 bytes
-rw-r--r--desktop/icons/ubuntu-mono-light/16/vkpc.pngbin0 -> 359 bytes
-rw-r--r--desktop/icons/ubuntu-mono-light/22/vkpc.pngbin0 -> 396 bytes
-rw-r--r--desktop/icons/ubuntu-mono-light/24/vkpc.pngbin0 -> 414 bytes
-rw-r--r--desktop/info.h13
-rwxr-xr-xdesktop/install_icons.sh16
-rw-r--r--desktop/main.c140
-rw-r--r--desktop/server.c182
-rw-r--r--desktop/server.h14
-rw-r--r--desktop/vector.c74
-rw-r--r--desktop/vector.h23
40 files changed, 557 insertions, 0 deletions
diff --git a/desktop/Makefile b/desktop/Makefile
new file mode 100644
index 0000000..49e6f7a
--- /dev/null
+++ b/desktop/Makefile
@@ -0,0 +1,29 @@
+LIBS = gtk+-2.0 glib-2.0 cairo pango gdk-pixbuf-2.0 atk libwebsockets x11
+CC = gcc
+CCFLAGS = -Wall -std=c99 -pthread `pkg-config --cflags --libs ${LIBS}`
+LDFLAGS = -lm
+BINARIES = vkpc
+
+all : vkpc
+
+vkpc : server.o grab.o vector.o main.o
+ ${CC} ${CCFLAGS} server.o grab.o vector.o main.o ${LDFLAGS} -o vkpc
+
+server.o : server.c
+ ${CC} ${CCFLAGS} -c server.c
+
+grab.o : grab.c
+ ${CC} ${CCFLAGS} -c grab.c
+
+vector.o : vector.c
+ ${CC} ${CCFLAGS} -c vector.c
+
+main.o : main.c
+ ${CC} ${CCFLAGS} -c main.c
+
+install:
+ cp vkpc /usr/bin
+ sh install_icons.sh
+
+clean:
+ rm -f $(BINARIES) *.o
diff --git a/desktop/grab.c b/desktop/grab.c
new file mode 100644
index 0000000..716b0c6
--- /dev/null
+++ b/desktop/grab.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XF86keysym.h>
+#include "grab.h"
+
+static int error_handler(Display *dpy, XErrorEvent *err) {
+ fprintf(stderr, "Failed to grab key!\n");
+ return 0;
+}
+
+void grab_init(void (*handler)(enum HotkeyEvent e)) {
+ Display *dpy = XOpenDisplay(0);
+ Window root = DefaultRootWindow(dpy);
+ XEvent ev;
+
+ struct Hotkey hotkeys[HOTKEYS_COUNT] = {
+ { HK_PAUSE, XKeysymToKeycode(dpy, XF86XK_AudioPause) },
+ { HK_PLAY, XKeysymToKeycode(dpy, XF86XK_AudioPlay) },
+ { HK_NEXT, XKeysymToKeycode(dpy, XF86XK_AudioNext) },
+ { HK_PREV, XKeysymToKeycode(dpy, XF86XK_AudioPrev) }
+ };
+
+ XSetErrorHandler(error_handler);
+
+ for (int i = 0; i < HOTKEYS_COUNT; i++) {
+ XGrabKey(dpy, hotkeys[i].keycode, 0, root, false, GrabModeAsync, GrabModeAsync);
+ }
+
+ XSelectInput(dpy, root, KeyPressMask);
+ while (true) {
+ XNextEvent(dpy, &ev);
+
+ switch (ev.type) {
+ case KeyPress: ;
+ for (int i = 0; i < HOTKEYS_COUNT; i++) {
+ if (ev.xkey.keycode == hotkeys[i].keycode) {
+ (*handler)(hotkeys[i].event);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ XCloseDisplay(dpy);
+}
diff --git a/desktop/grab.h b/desktop/grab.h
new file mode 100644
index 0000000..762f9d8
--- /dev/null
+++ b/desktop/grab.h
@@ -0,0 +1,15 @@
+#ifndef GRAB_H__
+#define GRAB_H__
+
+enum HotkeyEvent {
+ HK_PREV, HK_NEXT, HK_PAUSE, HK_PLAY,
+ HOTKEYS_COUNT
+};
+struct Hotkey {
+ enum HotkeyEvent event;
+ int keycode;
+};
+
+void grab_init();
+
+#endif
diff --git a/desktop/icons/Faenza-Radiance/apps/16/vkpc.png b/desktop/icons/Faenza-Radiance/apps/16/vkpc.png
new file mode 100644
index 0000000..1eaf260
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/16/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/22/vkpc.png b/desktop/icons/Faenza-Radiance/apps/22/vkpc.png
new file mode 100644
index 0000000..03ec012
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/22/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/24/vkpc.png b/desktop/icons/Faenza-Radiance/apps/24/vkpc.png
new file mode 100644
index 0000000..ad1fc6c
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/24/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/32/vkpc.png b/desktop/icons/Faenza-Radiance/apps/32/vkpc.png
new file mode 100644
index 0000000..4107fc3
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/32/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/48/vkpc.png b/desktop/icons/Faenza-Radiance/apps/48/vkpc.png
new file mode 100644
index 0000000..041f8a6
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/48/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/64/vkpc.png b/desktop/icons/Faenza-Radiance/apps/64/vkpc.png
new file mode 100644
index 0000000..ba1980d
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/64/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza-Radiance/apps/96/vkpc.png b/desktop/icons/Faenza-Radiance/apps/96/vkpc.png
new file mode 100644
index 0000000..853b46d
--- /dev/null
+++ b/desktop/icons/Faenza-Radiance/apps/96/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/16/vkpc.png b/desktop/icons/Faenza/apps/16/vkpc.png
new file mode 100644
index 0000000..29e3991
--- /dev/null
+++ b/desktop/icons/Faenza/apps/16/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/22/vkpc.png b/desktop/icons/Faenza/apps/22/vkpc.png
new file mode 100644
index 0000000..73daaa8
--- /dev/null
+++ b/desktop/icons/Faenza/apps/22/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/24/vkpc.png b/desktop/icons/Faenza/apps/24/vkpc.png
new file mode 100644
index 0000000..3f1ca5e
--- /dev/null
+++ b/desktop/icons/Faenza/apps/24/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/32/vkpc.png b/desktop/icons/Faenza/apps/32/vkpc.png
new file mode 100644
index 0000000..3502985
--- /dev/null
+++ b/desktop/icons/Faenza/apps/32/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/48/vkpc.png b/desktop/icons/Faenza/apps/48/vkpc.png
new file mode 100644
index 0000000..3f9ffa5
--- /dev/null
+++ b/desktop/icons/Faenza/apps/48/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/64/vkpc.png b/desktop/icons/Faenza/apps/64/vkpc.png
new file mode 100644
index 0000000..7278517
--- /dev/null
+++ b/desktop/icons/Faenza/apps/64/vkpc.png
Binary files differ
diff --git a/desktop/icons/Faenza/apps/96/vkpc.png b/desktop/icons/Faenza/apps/96/vkpc.png
new file mode 100644
index 0000000..d0e8b61
--- /dev/null
+++ b/desktop/icons/Faenza/apps/96/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/128x128/apps/vkpc.png b/desktop/icons/hicolor/128x128/apps/vkpc.png
new file mode 100644
index 0000000..7c5f49e
--- /dev/null
+++ b/desktop/icons/hicolor/128x128/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/22x22/apps/vkpc.png b/desktop/icons/hicolor/22x22/apps/vkpc.png
new file mode 100644
index 0000000..0f9186c
--- /dev/null
+++ b/desktop/icons/hicolor/22x22/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/24x24/apps/vkpc.png b/desktop/icons/hicolor/24x24/apps/vkpc.png
new file mode 100644
index 0000000..18b2b38
--- /dev/null
+++ b/desktop/icons/hicolor/24x24/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/32x32/apps/vkpc.png b/desktop/icons/hicolor/32x32/apps/vkpc.png
new file mode 100644
index 0000000..8cad27c
--- /dev/null
+++ b/desktop/icons/hicolor/32x32/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/36x36/apps/vkpc.png b/desktop/icons/hicolor/36x36/apps/vkpc.png
new file mode 100644
index 0000000..6448800
--- /dev/null
+++ b/desktop/icons/hicolor/36x36/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/40x40/apps/vkpc.png b/desktop/icons/hicolor/40x40/apps/vkpc.png
new file mode 100644
index 0000000..df33c09
--- /dev/null
+++ b/desktop/icons/hicolor/40x40/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/48x48/apps/vkpc.png b/desktop/icons/hicolor/48x48/apps/vkpc.png
new file mode 100644
index 0000000..d34540e
--- /dev/null
+++ b/desktop/icons/hicolor/48x48/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/64x64/apps/vkpc.png b/desktop/icons/hicolor/64x64/apps/vkpc.png
new file mode 100644
index 0000000..f94329a
--- /dev/null
+++ b/desktop/icons/hicolor/64x64/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/72x72/apps/vkpc.png b/desktop/icons/hicolor/72x72/apps/vkpc.png
new file mode 100644
index 0000000..3f26601
--- /dev/null
+++ b/desktop/icons/hicolor/72x72/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/hicolor/96x96/apps/vkpc.png b/desktop/icons/hicolor/96x96/apps/vkpc.png
new file mode 100644
index 0000000..b05a855
--- /dev/null
+++ b/desktop/icons/hicolor/96x96/apps/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-dark/16/vkpc.png b/desktop/icons/ubuntu-mono-dark/16/vkpc.png
new file mode 100644
index 0000000..440e6b9
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-dark/16/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-dark/22/vkpc.png b/desktop/icons/ubuntu-mono-dark/22/vkpc.png
new file mode 100644
index 0000000..6781df2
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-dark/22/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-dark/24/vkpc.png b/desktop/icons/ubuntu-mono-dark/24/vkpc.png
new file mode 100644
index 0000000..e57de23
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-dark/24/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-light/16/vkpc.png b/desktop/icons/ubuntu-mono-light/16/vkpc.png
new file mode 100644
index 0000000..dfb7295
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-light/16/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-light/22/vkpc.png b/desktop/icons/ubuntu-mono-light/22/vkpc.png
new file mode 100644
index 0000000..adc4436
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-light/22/vkpc.png
Binary files differ
diff --git a/desktop/icons/ubuntu-mono-light/24/vkpc.png b/desktop/icons/ubuntu-mono-light/24/vkpc.png
new file mode 100644
index 0000000..e04c84d
--- /dev/null
+++ b/desktop/icons/ubuntu-mono-light/24/vkpc.png
Binary files differ
diff --git a/desktop/info.h b/desktop/info.h
new file mode 100644
index 0000000..f13bbdc
--- /dev/null
+++ b/desktop/info.h
@@ -0,0 +1,13 @@
+#ifndef INFO_H__
+#define INFO_H__
+
+#define APP_NAME "VK Player Controller"
+#define APP_ABOUT "Use media buttons to switch between tracks."
+#define APP_VERSION "0.1"
+#define APP_AUTHOR "Eugene Z. <ch1p@ch1p.com>"
+#define APP_URL "http://ch1p.com/vkpc/"
+
+#define SERVER_PORT 52178
+#define SERVER_HOST "localhost"
+
+#endif
diff --git a/desktop/install_icons.sh b/desktop/install_icons.sh
new file mode 100755
index 0000000..cbdb4e1
--- /dev/null
+++ b/desktop/install_icons.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ICONS_PATH=/usr/share/icons
+ICONS=( "Faenza" "Faenza-Radiance" "hicolor" "ubuntu-mono-dark" "ubuntu-mono-light" )
+if [[ $EUID -ne 0 ]]; then
+ echo "This script must be run as root." 1>&2
+ exit 1
+fi
+
+for i in "${ICONS[@]}"
+do
+ if [ -d "${ICONS_PATH}/$i" ]; then
+ cp -r ${DIR}/icons/$1/* ${ICONS_PATH}/$1
+ fi
+done
diff --git a/desktop/main.c b/desktop/main.c
new file mode 100644
index 0000000..8703991
--- /dev/null
+++ b/desktop/main.c
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <gtk/gtk.h>
+
+#include "info.h"
+#include "server.h"
+#include "grab.h"
+
+static GtkStatusIcon *tray_icon;
+static GtkWidget *menu;
+
+enum server_last_cmd_enum server_last_cmd = NONE;
+static pthread_t grab_thread;
+static pthread_t server_thread;
+
+pthread_mutex_t server_last_cmd_mutex;
+
+void tray_icon_on_click(GtkStatusIcon *status_icon, gpointer user_data) {
+ // left-click
+}
+
+void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data) {
+ // right-click
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, activate_time);
+}
+
+void menu_about(GtkWidget *widget, gpointer data) {
+ GtkWidget *about_dialog;
+
+ const gchar *authors[] = {
+ APP_AUTHOR,
+ NULL
+ };
+
+ about_dialog = gtk_about_dialog_new();
+ gtk_about_dialog_set_version((GtkAboutDialog *)about_dialog, APP_VERSION);
+ gtk_about_dialog_set_authors((GtkAboutDialog *)about_dialog, authors);
+ gtk_about_dialog_set_comments((GtkAboutDialog *)about_dialog, (const gchar *)APP_ABOUT);
+ gtk_about_dialog_set_name((GtkAboutDialog *)about_dialog, APP_NAME);
+ gtk_about_dialog_set_website((GtkAboutDialog *)about_dialog, APP_URL);
+
+ g_signal_connect_swapped(about_dialog, "response", G_CALLBACK(gtk_widget_hide), about_dialog);
+
+ gtk_widget_show(about_dialog);
+}
+
+void menu_quit(GtkWidget *widget, gpointer data) {
+ // quit app
+ exit(0);
+}
+
+void create_tray_icon() {
+ tray_icon = gtk_status_icon_new();
+
+ g_signal_connect(G_OBJECT(tray_icon), "activate",
+ G_CALLBACK(tray_icon_on_click), NULL);
+ g_signal_connect(G_OBJECT(tray_icon), "popup-menu",
+ G_CALLBACK(tray_icon_on_menu), NULL);
+
+ gtk_status_icon_set_from_icon_name(tray_icon, "vkpc");
+ gtk_status_icon_set_tooltip(tray_icon, APP_NAME);
+ gtk_status_icon_set_visible(tray_icon, true);
+}
+
+void create_menu() {
+ GtkWidget *item;
+ menu = gtk_menu_new();
+
+ // About
+ item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ gtk_menu_item_set_label((GtkMenuItem *)item, "About");
+ g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_about), NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+
+ // Quit
+ item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ gtk_menu_item_set_label((GtkMenuItem *)item, "Quit");
+ g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_quit), NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+}
+
+void handle_hotkeys(enum HotkeyEvent e) {
+ pthread_mutex_lock(&server_last_cmd_mutex);
+ switch (e) {
+ case HK_PLAY:
+ server_last_cmd = PLAY;
+ break;
+
+ case HK_PAUSE:
+ server_last_cmd = PAUSE;
+ break;
+
+ case HK_NEXT:
+ server_last_cmd = NEXT;
+ break;
+
+ case HK_PREV:
+ server_last_cmd = PREV;
+ break;
+
+ default:
+ break;
+ }
+ pthread_mutex_unlock(&server_last_cmd_mutex);
+}
+
+void start_grab() {
+ int rc = pthread_create(&grab_thread, NULL, (void *)grab_init, handle_hotkeys);
+ if (rc) {
+ fprintf(stderr, "ERROR creating grab_thread, code = %d\n", rc);
+ exit(-1);
+ }
+}
+
+void start_server() {
+ int rc = pthread_create(&server_thread, NULL, (void *)server_init, NULL);
+ if (rc) {
+ fprintf(stderr, "ERROR creating server_thread, code = %d\n", rc);
+ exit(-1);
+ }
+}
+
+int main(int argc, char **argv) {
+ pthread_mutex_init(&server_last_cmd_mutex, NULL);
+
+ start_grab();
+ start_server();
+
+ gtk_init(&argc, &argv);
+
+ create_tray_icon();
+ create_menu();
+
+ gtk_main();
+ return 0;
+}
diff --git a/desktop/server.c b/desktop/server.c
new file mode 100644
index 0000000..a3c308f
--- /dev/null
+++ b/desktop/server.c
@@ -0,0 +1,182 @@
+/**
+ * TODO: logging level
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libwebsockets.h>
+#include <stdbool.h>
+
+#include "server.h"
+#include "vector.h"
+
+#define SERVER_PORT 52178
+#define SERVER_HOST "localhost"
+
+static struct libwebsocket_context *context;
+static char *server_last_cmd_values[] = {
+ "none", "play", "pause", "next", "prev"
+};
+
+struct per_session_data {
+ bool established;
+ char *next_command;
+};
+struct session {
+ struct per_session_data *pss;
+ struct libwebsocket *wsi;
+};
+vector *sessions;
+
+static void add_session(struct libwebsocket *wsi, struct per_session_data *pss) {
+ struct session *s = malloc(sizeof(struct session));
+ s->wsi = wsi;
+ s->pss = pss;
+ vector_add(sessions, s);
+}
+
+static void delete_session(struct libwebsocket *wsi) {
+ for (int i = 0; i < vector_count(sessions); i++) {
+ struct session *s = vector_get(sessions, i);
+ if (s != NULL && s->wsi == wsi) {
+ printf("(delete_session) found, i=%d\n", i);
+ free(s);
+ vector_delete(sessions, i);
+ break;
+ }
+ }
+}
+
+static void send_command_to_all(char *command) {
+ printf("Got command: %s\n", command);
+ for (int i = 0; i < vector_count(sessions); i++) {
+ struct session *s = (struct session *)vector_get(sessions, i);
+ s->pss->next_command = command;
+ libwebsocket_callback_on_writable(context, s->wsi);
+ }
+}
+
+static int callback_http(struct libwebsocket_context *this,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user,
+ void *in,
+ size_t len)
+{
+ switch (reason) {
+ case LWS_CALLBACK_HTTP: ;
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+
+ case LWS_CALLBACK_HTTP_WRITEABLE: ;
+ char *response = "vkpc, world!";
+ libwebsocket_write(wsi, (unsigned char *)response, strlen(response), LWS_WRITE_HTTP);
+ return -1;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int callback_signaling(struct libwebsocket_context *this,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user,
+ void *in,
+ size_t len)
+{
+ struct per_session_data *pss = (struct per_session_data *)user;
+
+ switch (reason) {
+ case LWS_CALLBACK_ESTABLISHED:
+ lwsl_info("Connection established");
+
+ pss->established = true;
+ pss->next_command = NULL;
+ add_session(wsi, pss);
+
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ if (pss->next_command != NULL) {
+ int length = strlen(pss->next_command);
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + length + LWS_SEND_BUFFER_POST_PADDING];
+ unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
+
+ strcpy((char *)p, pss->next_command);
+ int m = libwebsocket_write(wsi, p, length, LWS_WRITE_TEXT);
+
+ if (m < length) {
+ lwsl_err("ERROR while writing %d bytes to socket\n", length);
+ return -1;
+ }
+
+ pss->next_command = NULL;
+ }
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+ lwsl_info("Received: %s, length: %d\n",
+ (char *)in, (int)strlen((char *)in));
+ break;
+
+ case LWS_CALLBACK_CLOSED:
+ lwsl_info("Connection closed\n");
+ delete_session(wsi);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct libwebsocket_protocols protocols[] = {
+ { "http-only", callback_http, 0, 0 },
+ { "signaling-protocol", callback_signaling, sizeof(struct per_session_data), 0 },
+ { NULL, NULL, 0 }
+};
+
+void server_init() {
+ sessions = vector_create();
+
+ struct lws_context_creation_info info;
+ memset(&info, 0, sizeof(info));
+
+ info.port = SERVER_PORT;
+ info.iface = SERVER_HOST;
+ info.protocols = protocols;
+ info.extensions = libwebsocket_get_internal_extensions();
+ info.ssl_cert_filepath = NULL;
+ info.ssl_private_key_filepath = NULL;
+ info.gid = -1;
+ info.uid = -1;
+ info.options = 0;
+
+ context = libwebsocket_create_context(&info);
+ if (context == NULL) {
+ fprintf(stderr, "libwebsocket init failed\n");
+ return;
+ }
+
+ enum server_last_cmd_enum last_cmd = NONE;
+ while (1) {
+ pthread_mutex_lock(&server_last_cmd_mutex);
+ last_cmd = server_last_cmd;
+ server_last_cmd = NONE;
+ pthread_mutex_unlock(&server_last_cmd_mutex);
+
+ if (last_cmd != NONE) {
+ send_command_to_all(server_last_cmd_values[last_cmd]);
+ }
+
+ libwebsocket_service(context, 50);
+ }
+
+ libwebsocket_context_destroy(context);
+ return;
+}
diff --git a/desktop/server.h b/desktop/server.h
new file mode 100644
index 0000000..1b7af3f
--- /dev/null
+++ b/desktop/server.h
@@ -0,0 +1,14 @@
+#include <pthread.h>
+
+#ifndef SERVER_H__
+#define SERVER_H__
+
+enum server_last_cmd_enum {
+ NONE = 0, PLAY, PAUSE, NEXT, PREV
+};
+extern enum server_last_cmd_enum server_last_cmd;
+extern pthread_mutex_t server_last_cmd_mutex;
+
+void server_init();
+
+#endif
diff --git a/desktop/vector.c b/desktop/vector.c
new file mode 100644
index 0000000..16b8804
--- /dev/null
+++ b/desktop/vector.c
@@ -0,0 +1,74 @@
+/**
+ * Based on https://gist.github.com/EmilHernvall/953968
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vector.h"
+
+vector * vector_create() {
+ vector *v;
+ v = malloc(sizeof(vector));
+
+ v->data = NULL;
+ v->size = 0;
+ v->count = 0;
+
+ return v;
+}
+
+int vector_count(vector *v) {
+ return v->count;
+}
+
+void vector_add(vector *v, void *e) {
+ if (v->size == 0) {
+ v->size = 10;
+ v->data = malloc(sizeof(void *) * v->size);
+ memset(v->data, '\0', sizeof(void *) * v->size);
+ }
+
+ if (v->size == v->count) {
+ v->size += 10;
+ v->data = realloc(v->data, sizeof(void *) * v->size);
+ }
+
+ v->data[v->count++] = e;
+}
+
+void vector_set(vector *v, int index, void *e) {
+ if (index >= v->count) {
+ return;
+ }
+
+ v->data[index] = e;
+}
+
+void * vector_get(vector *v, int index) {
+ if (index >= v->count) {
+ return NULL;
+ }
+
+ return v->data[index];
+}
+
+void vector_delete(vector *v, int index) {
+ if (index >= v->count) {
+ return;
+ }
+
+ for (int i = index+1; i < v->count; i++) {
+ v->data[i-1] = v->data[i];
+ }
+
+ v->data[--v->count] = NULL;
+}
+
+void vector_free_data(vector *v) {
+ free(v->data);
+}
+
+void vector_free(vector *v) {
+ free(v);
+}
diff --git a/desktop/vector.h b/desktop/vector.h
new file mode 100644
index 0000000..c9e9c98
--- /dev/null
+++ b/desktop/vector.h
@@ -0,0 +1,23 @@
+/**
+ * Based on https://gist.github.com/EmilHernvall/953968
+ */
+
+#ifndef VECTOR_H__
+#define VECTOR_H__
+
+typedef struct vector_ {
+ void **data;
+ int size;
+ int count;
+} vector;
+
+vector * vector_create();
+int vector_count(vector *);
+void vector_add(vector *, void *);
+void vector_set(vector *, int, void *);
+void *vector_get(vector *, int);
+void vector_delete(vector*, int);
+void vector_free_data(vector *);
+void vector_free(vector *);
+
+#endif