SRC += json.c
SRC += Sidebar.c
SRC += xdnd.c
+SRC += playlist.c
OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/application/%.$(OBJ_EXT))
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+
+#include "xdnd.h"
static void sidebar_class_init(void);
Boolean sidebar_acceptfocus(Widget widget, Time *time);
static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
+static void xdndEnterAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
+
+static void sidebar_xdnd_callback(Widget w, XtPointer udata, XtPointer cdata);
+
static XtResource resources[] = {
static XtActionsRec actionslist[] = {
{"focusIn", FocusInAP},
+ {"xdnd_enter", xdndEnterAP},
{"NULL", NULL}
};
-static char defaultTranslations[] = "<FocusIn>: focusIn()";
+static char defaultTranslations[] = "<FocusIn>: focusIn()\n"
+ "<Message>XdndEnter: xdnd_enter()\n";
static XtResource constraints[] = {};
}
+static int xdnd_initialized = 0;
+
static void sidebar_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) {
(xmManagerClassRec.core_class.realize)(widget, mask, attributes);
-
+ if(!xdnd_initialized) {
+ XdndInit(XtDisplay(widget), XtWidgetToApplicationContext(widget), sidebar_xdnd_callback, widget);
+ }
+ XdndEnable(widget);
}
static void sidebar_expose(Widget widget, XEvent *event, Region region) {
static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
-
+ printf("focusin\n");
+ fflush(stdout);
+}
+
+static void xdndEnterAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
+ printf("xdndEnterAP\n");
+ fflush(stdout);
}
+
+static void sidebar_xdnd_callback(Widget w, XtPointer udata, XtPointer cdata) {
+ printf("xdnd\n");
+ fflush(stdout);
+
+ char *urilist = udata;
+
+ size_t len = strlen(urilist);
+
+ size_t start = 0;
+ if(len > 7 && !memcmp(urilist, "file://", 7)) {
+ start = 7;
+ }
+
+ int err = 0;
+
+ // urldecode
+ char *path = malloc(len + 1);
+ int k = 0;
+ for(int i=start;i<len;i++) {
+ char c = urilist[i];
+ if(c == '%') {
+ if(i + 2 < len) {
+ char code[3];
+ code[0] = urilist[i+1];
+ code[1] = urilist[i+2];
+ code[2] = '\0';
+
+ errno = 0;
+ char *end = NULL;
+ int ascii = (int)strtol(code, &end, 16);
+ if(errno == 0 && end == &code[2]) {
+ path[k] = ascii;
+ i += 2;
+ } else {
+ err = 1;
+ break;
+ }
+ } else {
+ err = 1;
+ break;
+ }
+ } else if(c == '\n' || c == '\r') {
+ break;
+ } else {
+ path[k] = c;
+ }
+
+ k++;
+ }
+ path[k] = '\0';
+
+ // add file
+ // TODO
+
+ free(path);
+}
+
+
/* --------------------------- public --------------------------- */
Widget CreateSidebar(
} SidebarClassRec;
typedef struct SidebarPart {
- int a;
+ int a; // placeholder
} SidebarPart;
typedef struct SidebarRec {
#include "json.h"
#include "utils.h"
#include "settings.h"
+#include "playlist.h"
extern char **environ;
}
}
-static Boolean open_next_file(XtPointer data) {
- char *file = data;
+static Boolean play_next(XtPointer data) {
MainWindow *win = GetMainWindow();
- if(win->file) {
- free(file);
- }
- win->file = file;
- PlayerOpenFile(win);
+ PlayListPlayNext(win, false);
return 0;
}
void PlayerEOF(Player *p) {
MainWindow *win = GetMainWindow();
- if(win->repeatTrack) {
+ if(win->playlist.repeatTrack) {
char *cmd = "{ \"command\": [\"set_property\", \"playback-time\", 0] }\n";
write(p->ipc, cmd, strlen(cmd));
- } else if(win->autoplayFolder) {
- char *next_file = util_find_next_file(win->file);
- if(next_file) {
- AppExecProc(open_next_file, next_file);
- }
+ } else {
+ AppExecProc(play_next, NULL);
}
}
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "playlist.h"
+
+#include "player.h"
+#include "utils.h"
+
+void PlayListInit(MainWindow *win) {
+ win->playlist.current_track = -1;
+}
+
+void PlayListAddFile(MainWindow *win, const char *file) {
+ char *f = strdup(file);
+ win->playlist.tracks = ucx_list_append(win->playlist.tracks, f);
+}
+
+void PlayListPlayNext(MainWindow *win, bool force) {
+ UcxList *tracks = win->playlist.tracks;
+ if(!tracks) return;
+ size_t len = ucx_list_size(tracks);
+
+ int current = win->playlist.current_track;
+ if(win->playlist.repeatTrack) {
+ if(force) {
+ current++;
+ }
+ } else if(current < len) {
+ current++;
+ } else if(win->playlist.autoplayFolder) {
+ char *next_file = util_find_next_file(win->file);
+ win->playlist.tracks = ucx_list_append(win->playlist.tracks, next_file);
+ current = len;
+ } else {
+ current = 0;
+ }
+
+ win->playlist.current_track = current;
+
+ UcxList *fileElm = ucx_list_get(tracks, current);
+ if(!fileElm) {
+ return;
+ }
+ win->file = fileElm->data;
+
+ PlayerOpenFile(win);
+}
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include "window.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void PlayListInit(MainWindow *win);
+
+void PlayListAddFile(MainWindow *win, const char *file);
+
+void PlayListPlayNext(MainWindow *win, bool force);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PLAYLIST_H */
+
#include "window.h"
#include "main.h"
#include "player.h"
+#include "playlist.h"
+#include "xdnd.h"
#include "Fsb.h"
#include "Sidebar.h"
static void WindowRealized(MainWindow *win) {
char *open_file = GetOpenFileArg();
if(open_file) {
- size_t len = strlen(open_file);
- char *file = XtMalloc(len+1);
- memcpy(file, open_file, len);
- file[len] = 0;
- WindowSetFile(win, file);
- PlayerOpenFile(win);
+ PlayListAddFile(win, open_file);
+ PlayListPlayNext(win, true);
CleanOpenFileArg();
}
if(!blank_cursor_init) {
init_blank_cursor(win->player_widget);
}
+
+ XdndEnable(win->window);
}
static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) {
}
}
+
+
MainWindow* WindowCreate(Display *display) {
Arg args[32];
int n;
MainWindow *window = malloc(sizeof(MainWindow));
memset(window, 0, sizeof(MainWindow));
main_window = window;
-
+
// toplevel window
n = 0;
XtSetArg(args[n], XmNtitle, APP_NAME); n++;
XtSetArg(args[n], XmNwidth, 300); n++;
window->sidebar = CreateSidebar(container, "sidebar", args, n);
//XtManageChild(window->sidebar);
-
+
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
// get F keycode
keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
+
+ PlayListInit(window);
+
return window;
}
}
}
-void WindowSetFile(MainWindow *win, char *file) {
- if(win->file) {
- XtFree(win->file);
- }
- win->file = file;
-}
-
static void filedialog_end(
Widget widget,
MainWindow *data,
if(selection->value) {
XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
if(value) {
- WindowSetFile(data, value);
- // no need to free the value, because it is stored in MainWindow
-
- PlayerOpenFile(data);
+ PlayListAddFile(data, value);
+ PlayListPlayNext(data, true);
+ XtFree(value);
}
}
filedialog_end(widget, data, NULL);
static void PlayRepeatCB(Widget w, void *udata, void *cdata) {
MainWindow *win = udata;
- win->repeatTrack = XmToggleButtonGadgetGetState(w);
- win->repeatList = 0;
- win->autoplayFolder = 0;
+ win->playlist.repeatTrack = XmToggleButtonGadgetGetState(w);
+ win->playlist.repeatList = 0;
+ win->playlist.autoplayFolder = 0;
XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
}
static void PlayRepeatListCB(Widget w, void *udata, void *cdata) {
MainWindow *win = udata;
- win->repeatList = XmToggleButtonGadgetGetState(w);
- win->repeatTrack = 0;
- win->autoplayFolder = 0;
+ win->playlist.repeatList = XmToggleButtonGadgetGetState(w);
+ win->playlist.repeatTrack = 0;
+ win->playlist.autoplayFolder = 0;
XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
}
static void PlayAutoPlayCB(Widget w, void *udata, void *cdata) {
MainWindow *win = udata;
- win->autoplayFolder = XmToggleButtonGadgetGetState(w);
- win->repeatTrack = 0;
- win->repeatList = 0;
+ win->playlist.autoplayFolder = XmToggleButtonGadgetGetState(w);
+ win->playlist.repeatTrack = 0;
+ win->playlist.repeatList = 0;
XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
}
XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, win->sidebar, NULL);
}
+
+
+
+
#include <Xm/XmAll.h>
#include <stdbool.h>
#include <unistd.h>
+#include <ucx/list.h>
#ifdef __cplusplus
extern "C" {
int height;
int osd_height;
} Player;
+
+typedef struct {
+ UcxList *tracks;
+ int current_track;
+ Boolean repeatTrack;
+ Boolean repeatList;
+ Boolean autoplayFolder;
+} PlayList;
+
typedef struct MainWindow {
Widget window;
Widget menubar;
Widget playAutoPlayButton;
Widget viewSidebarButton;
+ PlayList playlist;
+
Time player_event_time;
Time button_press_time;
double motion_playback_time;
int mouse_x;
int mouse_y;
- Boolean repeatTrack;
- Boolean repeatList;
- Boolean autoplayFolder;
+
} MainWindow;
MainWindow* WindowCreate(Display *dp);
void WindowMenubarSetVisible(MainWindow *win, bool visible);
-void WindowSetFile(MainWindow *win, char *file);
-
void WindowAdjustAspectRatio(MainWindow *win);
void WindowClosePlayer(MainWindow *win);
void WindowHideSidebar(MainWindow *win);
void WindowShowSidebar(MainWindow *win);
+
#ifdef __cplusplus
}
#endif
}
static void xdnd_enter(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
- //printf("xdnd_enter\n");
+ printf("xdnd_enter\n");
+ fflush(stdout);
XtGetSelectionValue(w, XdndSelection, selType, checkSelectionValue, NULL, 0);
}
dropData = dropCBData;
}
-void XdndEnable(Widget w) {
+void XdndEnable(Widget w) {
int version = 4;
XChangeProperty(
XtDisplay(w),