Skip to content

Instantly share code, notes, and snippets.

@henkman
Last active March 6, 2025 16:03
Show Gist options
  • Save henkman/87140ac06c149fcf63ef5a6f656797e9 to your computer and use it in GitHub Desktop.
Save henkman/87140ac06c149fcf63ef5a6f656797e9 to your computer and use it in GitHub Desktop.
basic video viewer using fltk & embedded mpv. just drag&drop a folder and use left and right arrow. s to shuffle
// g++ -O2 -s -o pview main.cc -lfltk -lfltk_images -lmpv
#include <stdio.h>
#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/fl_draw.H>
#include <FL/x.H>
#include <mpv/client.h>
#if __linux__
#include <unistd.h>
#elif _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
class MPVPlayer : public Fl_Window {
public:
MPVPlayer(int x, int y, int w, int h, const char *label = "")
: Fl_Window(x, y, w, h, label) {}
~MPVPlayer() { mpv_terminate_destroy(mpv); }
void init() {
mpv = mpv_create();
uint64_t wid = fl_xid(this);
mpv_set_property(mpv, "wid", MPV_FORMAT_INT64, &wid);
mpv_set_property_string(mpv, "loop-file", "inf");
mpv_initialize(mpv);
}
void startFile(const char *file) {
if(!mpv)
init();
const char *mpv_cmd[] = {"loadfile", file, NULL};
mpv_command(mpv, mpv_cmd);
}
void playlistNext() {
if(!mpv)
return;
const char *mpv_cmd[] = {"playlist-next", NULL};
mpv_command(mpv, mpv_cmd);
}
void playlistPrev() {
if(!mpv)
return;
const char *mpv_cmd[] = {"playlist-prev", NULL};
mpv_command(mpv, mpv_cmd);
}
void playlistShuffle() {
if(!mpv)
return;
const char *mpv_cmd[] = {"playlist-shuffle", NULL};
mpv_command(mpv, mpv_cmd);
}
void openFileManager() {
if(!mpv)
return;
const char *path = mpv_get_property_string(mpv, "path");
#if __linux__
#define FILE_MANAGER "nemo"
{
char cmd[512] = {0};
sprintf(cmd, "/bin/bash -c '" FILE_MANAGER " \"%s\"'", path);
FILE *pf = popen(cmd, "r");
pclose(pf);
}
#elif _WIN32
{
char args[1024] = {0};
sprintf(args, "/select, \"%s\"", path);
ShellExecute(NULL, "explorer", args, NULL, NULL, 0);
}
#else
#endif
mpv_free((void *)path);
}
struct Timeout {
MPVPlayer *player;
double timeoutseconds;
};
static void timeout_cb(void *data) {
Timeout *td = (Timeout *)data;
td->player->playlistNext();
Fl::repeat_timeout(td->timeoutseconds, MPVPlayer::timeout_cb, data);
}
int handle(int event) override {
switch (event) {
case FL_KEYBOARD: {
switch (Fl::event_key()) {
case 'n': // fallthrough
case FL_Right: {
playlistNext();
} break;
case 'p': // fallthrough
case FL_Left: {
playlistPrev();
} break;
case 's': {
playlistShuffle();
} break;
case 'o': {
openFileManager();
} break;
case 'a': {
autoforward = !autoforward;
if (autoforward)
Fl::add_timeout(timeout.timeoutseconds, MPVPlayer::timeout_cb,
&timeout);
else
Fl::remove_timeout(MPVPlayer::timeout_cb, &timeout);
} break;
case FL_Up: {
timeout.timeoutseconds += 0.5;
} break;
case FL_Down: {
if (timeout.timeoutseconds > 1.0)
timeout.timeoutseconds -= 0.5;
} break;
}
} break;
case FL_PASTE: {
const char *file = Fl::event_text();
char *fixed = strdup(file);
size_t len = strlen(fixed);
fixed[len - 1] = 0;
startFile(fixed);
free(fixed);
} // fallthrough
case FL_DND_DRAG: // fallthrough
case FL_DND_ENTER: // fallthrough
case FL_DND_LEAVE: // fallthrough
case FL_DND_RELEASE: // fallthrough
case FL_FOCUS: // fallthrough
return 1;
}
return Fl_Window::handle(event);
}
void draw() override {
if(mpv)
Fl_Window::draw();
else {
fl_color(FL_BLACK);
fl_font(FL_HELVETICA, 50);
const char *text = "drag & drop a file/folder here";
fl_draw(text, (w()-fl_width(text))/2, h()/2);
}
}
private:
mpv_handle *mpv = nullptr;
bool autoforward = false;
Timeout timeout = {this, 3.0};
};
static char *initialFile = NULL;
static int args_cb(int argc, char **argv, int &i) {
if (strcmp("-if", argv[i]) == 0) {
if (i < argc - 1 && argv[i + 1] != 0) {
initialFile = argv[i + 1];
i += 2;
return 2;
}
}
return 0;
}
int main(int argc, char **argv) {
int i = 0;
Fl::args(argc, argv, i, &args_cb);
auto win = Fl_Double_Window(1280, 720, "pview");
auto player = MPVPlayer(0, 0, 1280, 720);
win.icon(new Fl_PNG_Image("Awicons-Vista-Artistic-Folder-my-video.256.png"));
win.add(player);
win.resizable(player);
win.end();
win.show(argc, argv);
if (initialFile != NULL)
player.startFile(initialFile);
Fl::focus(&player);
return (Fl::run());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment