Compare commits
6 Commits
cmd_macos.
...
cmd_macos
Author | SHA1 | Date | |
---|---|---|---|
e2ac996183 | |||
5e4ccfd832 | |||
53b6ee2cf4 | |||
26213f1031 | |||
96b5067cbf | |||
421e4be399 |
@ -125,12 +125,12 @@ dist-win64: build-server build-win64 build-win64-noconsole
|
|||||||
cp prebuilt-deps/SDL2-2.0.8/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/SDL2-2.0.8/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
|
||||||
zip-win32: dist-win32
|
zip-win32: dist-win32
|
||||||
cd "$(DIST)"; \
|
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
||||||
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)"
|
zip -r "../$(WIN32_TARGET)" .
|
||||||
|
|
||||||
zip-win64: dist-win64
|
zip-win64: dist-win64
|
||||||
cd "$(DIST)"; \
|
cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
|
||||||
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)"
|
zip -r "../$(WIN64_TARGET)" .
|
||||||
|
|
||||||
sums:
|
sums:
|
||||||
cd "$(DIST)"; \
|
cd "$(DIST)"; \
|
||||||
|
40
README.md
40
README.md
@ -315,26 +315,26 @@ Also see [issue #14].
|
|||||||
|
|
||||||
## Shortcuts
|
## Shortcuts
|
||||||
|
|
||||||
| Action | Shortcut |
|
| Action | Shortcut | Shortcut (macOS)
|
||||||
| -------------------------------------- |:---------------------------- |
|
| -------------------------------------- |:----------------------------- |:-----------------------------
|
||||||
| Switch fullscreen mode | `Ctrl`+`f` |
|
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
|
||||||
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` |
|
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
|
||||||
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ |
|
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
|
||||||
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ |
|
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
|
||||||
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ |
|
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
|
||||||
| Click on `APP_SWITCH` | `Ctrl`+`s` |
|
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
|
||||||
| Click on `MENU` | `Ctrl`+`m` |
|
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
|
||||||
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ (`Cmd`+`↑` on macOS) |
|
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
|
||||||
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on macOS) |
|
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
|
||||||
| Click on `POWER` | `Ctrl`+`p` |
|
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
|
||||||
| Power on | _Right-click²_ |
|
| Power on | _Right-click²_ | _Right-click²_
|
||||||
| Turn device screen off (keep mirroring)| `Ctrl`+`o` |
|
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
|
||||||
| Expand notification panel | `Ctrl`+`n` |
|
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
|
||||||
| Collapse notification panel | `Ctrl`+`Shift`+`n` |
|
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
|
||||||
| Copy device clipboard to computer | `Ctrl`+`c` |
|
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
|
||||||
| Paste computer clipboard to device | `Ctrl`+`v` |
|
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
|
||||||
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` |
|
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
|
||||||
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
|
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
|
||||||
|
|
||||||
_¹Double-click on black borders to remove them._
|
_¹Double-click on black borders to remove them._
|
||||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||||
|
@ -150,6 +150,9 @@ tests = [
|
|||||||
'tests/test_device_msg_deserialize.c',
|
'tests/test_device_msg_deserialize.c',
|
||||||
'src/device_msg.c'
|
'src/device_msg.c'
|
||||||
]],
|
]],
|
||||||
|
['test_queue', [
|
||||||
|
'tests/test_queue.c',
|
||||||
|
]],
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
'src/str_util.c'
|
'src/str_util.c'
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// To define a circular buffer type of 20 ints:
|
// To define a circular buffer type of 20 ints:
|
||||||
// typedef CBUF(int, 20) my_cbuf_t;
|
// struct cbuf_int CBUF(int, 20);
|
||||||
//
|
//
|
||||||
// data has length CAP + 1 to distinguish empty vs full.
|
// data has length CAP + 1 to distinguish empty vs full.
|
||||||
#define CBUF(TYPE, CAP) { \
|
#define CBUF(TYPE, CAP) { \
|
||||||
@ -35,7 +35,7 @@
|
|||||||
(PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \
|
(PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \
|
||||||
} \
|
} \
|
||||||
ok; \
|
ok; \
|
||||||
}) \
|
})
|
||||||
|
|
||||||
#define cbuf_take(PCBUF, PITEM) \
|
#define cbuf_take(PCBUF, PITEM) \
|
||||||
({ \
|
({ \
|
||||||
|
@ -242,16 +242,27 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
||||||
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
||||||
|
|
||||||
|
// use Cmd on macOS, Ctrl on other platforms
|
||||||
|
#ifdef __APPLE__
|
||||||
|
bool cmd = !ctrl && meta;
|
||||||
|
#else
|
||||||
|
if (meta) {
|
||||||
|
// no shortcuts involve Meta on platforms other than macOS, and it must
|
||||||
|
// not be forwarded to the device
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool cmd = ctrl; // && !meta, already guaranteed
|
||||||
|
#endif
|
||||||
|
|
||||||
if (alt) {
|
if (alt) {
|
||||||
// no shortcut involves Alt or Meta, and they should not be forwarded
|
// no shortcuts involve Alt, and it must not be forwarded to the device
|
||||||
// to the device
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct controller *controller = input_manager->controller;
|
struct controller *controller = input_manager->controller;
|
||||||
|
|
||||||
// capture all Ctrl events
|
// capture all Ctrl events
|
||||||
if (ctrl | meta) {
|
if (ctrl || cmd) {
|
||||||
SDL_Keycode keycode = event->keysym.sym;
|
SDL_Keycode keycode = event->keysym.sym;
|
||||||
bool down = event->type == SDL_KEYDOWN;
|
bool down = event->type == SDL_KEYDOWN;
|
||||||
int action = down ? ACTION_DOWN : ACTION_UP;
|
int action = down ? ACTION_DOWN : ACTION_UP;
|
||||||
@ -259,63 +270,59 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
|
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
|
||||||
switch (keycode) {
|
switch (keycode) {
|
||||||
case SDLK_h:
|
case SDLK_h:
|
||||||
|
// Ctrl+h on all platform, since Cmd+h is already captured by
|
||||||
|
// the system on macOS to hide the window
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
if (control && ctrl && !meta && !shift && !repeat) {
|
||||||
action_home(controller, action);
|
action_home(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_b: // fall-through
|
case SDLK_b: // fall-through
|
||||||
case SDLK_BACKSPACE:
|
case SDLK_BACKSPACE:
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
if (control && cmd && !shift && !repeat) {
|
||||||
action_back(controller, action);
|
action_back(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
if (control && cmd && !shift && !repeat) {
|
||||||
action_app_switch(controller, action);
|
action_app_switch(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_m:
|
case SDLK_m:
|
||||||
|
// Ctrl+m on all platform, since Cmd+m is already captured by
|
||||||
|
// the system on macOS to minimize the window
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
if (control && ctrl && !meta && !shift && !repeat) {
|
||||||
action_menu(controller, action);
|
action_menu(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
if (control && ctrl && !meta && !shift && !repeat) {
|
if (control && cmd && !shift && !repeat) {
|
||||||
action_power(controller, action);
|
action_power(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_o:
|
case SDLK_o:
|
||||||
if (control && ctrl && !shift && !meta && down) {
|
if (control && cmd && !shift && down) {
|
||||||
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
|
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
#ifdef __APPLE__
|
if (control && cmd && !shift) {
|
||||||
if (control && !ctrl && meta && !shift) {
|
|
||||||
#else
|
|
||||||
if (control && ctrl && !meta && !shift) {
|
|
||||||
#endif
|
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_down(controller, action);
|
action_volume_down(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
#ifdef __APPLE__
|
if (control && cmd && !shift) {
|
||||||
if (control && !ctrl && meta && !shift) {
|
|
||||||
#else
|
|
||||||
if (control && ctrl && !meta && !shift) {
|
|
||||||
#endif
|
|
||||||
// forward repeated events
|
// forward repeated events
|
||||||
action_volume_up(controller, action);
|
action_volume_up(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
if (control && ctrl && !meta && !shift && !repeat && down) {
|
if (control && cmd && !shift && !repeat && down) {
|
||||||
request_device_clipboard(controller);
|
request_device_clipboard(controller);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_v:
|
case SDLK_v:
|
||||||
if (control && ctrl && !meta && !repeat && down) {
|
if (control && cmd && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
// store the text in the device clipboard
|
// store the text in the device clipboard
|
||||||
set_device_clipboard(controller);
|
set_device_clipboard(controller);
|
||||||
@ -326,29 +333,29 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
if (ctrl && !meta && !shift && !repeat && down) {
|
if (!shift && cmd && !repeat && down) {
|
||||||
screen_switch_fullscreen(input_manager->screen);
|
screen_switch_fullscreen(input_manager->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_x:
|
case SDLK_x:
|
||||||
if (ctrl && !meta && !shift && !repeat && down) {
|
if (!shift && cmd && !repeat && down) {
|
||||||
screen_resize_to_fit(input_manager->screen);
|
screen_resize_to_fit(input_manager->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (ctrl && !meta && !shift && !repeat && down) {
|
if (!shift && cmd && !repeat && down) {
|
||||||
screen_resize_to_pixel_perfect(input_manager->screen);
|
screen_resize_to_pixel_perfect(input_manager->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_i:
|
case SDLK_i:
|
||||||
if (ctrl && !meta && !shift && !repeat && down) {
|
if (!shift && cmd && !repeat && down) {
|
||||||
struct fps_counter *fps_counter =
|
struct fps_counter *fps_counter =
|
||||||
input_manager->video_buffer->fps_counter;
|
input_manager->video_buffer->fps_counter;
|
||||||
switch_fps_counter_state(fps_counter);
|
switch_fps_counter_state(fps_counter);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_n:
|
case SDLK_n:
|
||||||
if (control && ctrl && !meta && !repeat && down) {
|
if (control && cmd && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
collapse_notification_panel(controller);
|
collapse_notification_panel(controller);
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,6 +35,11 @@ struct args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void usage(const char *arg0) {
|
static void usage(const char *arg0) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# define CTRL_OR_CMD "Cmd"
|
||||||
|
#else
|
||||||
|
# define CTRL_OR_CMD "Ctrl"
|
||||||
|
#endif
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: %s [options]\n"
|
"Usage: %s [options]\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -115,13 +120,13 @@ static void usage(const char *arg0) {
|
|||||||
"\n"
|
"\n"
|
||||||
"Shortcuts:\n"
|
"Shortcuts:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+f\n"
|
" " CTRL_OR_CMD "+f\n"
|
||||||
" switch fullscreen mode\n"
|
" switch fullscreen mode\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+g\n"
|
" " CTRL_OR_CMD "+g\n"
|
||||||
" resize window to 1:1 (pixel-perfect)\n"
|
" resize window to 1:1 (pixel-perfect)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+x\n"
|
" " CTRL_OR_CMD "+x\n"
|
||||||
" Double-click on black borders\n"
|
" Double-click on black borders\n"
|
||||||
" resize window to remove black borders\n"
|
" resize window to remove black borders\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -129,48 +134,48 @@ static void usage(const char *arg0) {
|
|||||||
" Middle-click\n"
|
" Middle-click\n"
|
||||||
" click on HOME\n"
|
" click on HOME\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+b\n"
|
" " CTRL_OR_CMD "+b\n"
|
||||||
" Ctrl+Backspace\n"
|
" " CTRL_OR_CMD "+Backspace\n"
|
||||||
" Right-click (when screen is on)\n"
|
" Right-click (when screen is on)\n"
|
||||||
" click on BACK\n"
|
" click on BACK\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+s\n"
|
" " CTRL_OR_CMD "+s\n"
|
||||||
" click on APP_SWITCH\n"
|
" click on APP_SWITCH\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+m\n"
|
" Ctrl+m\n"
|
||||||
" click on MENU\n"
|
" click on MENU\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+Up\n"
|
" " CTRL_OR_CMD "+Up\n"
|
||||||
" click on VOLUME_UP\n"
|
" click on VOLUME_UP\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+Down\n"
|
" " CTRL_OR_CMD "+Down\n"
|
||||||
" click on VOLUME_DOWN\n"
|
" click on VOLUME_DOWN\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+p\n"
|
" " CTRL_OR_CMD "+p\n"
|
||||||
" click on POWER (turn screen on/off)\n"
|
" click on POWER (turn screen on/off)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Right-click (when screen is off)\n"
|
" Right-click (when screen is off)\n"
|
||||||
" power on\n"
|
" power on\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+o\n"
|
" " CTRL_OR_CMD "+o\n"
|
||||||
" turn device screen off (keep mirroring)\n"
|
" turn device screen off (keep mirroring)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+n\n"
|
" " CTRL_OR_CMD "+n\n"
|
||||||
" expand notification panel\n"
|
" expand notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+Shift+n\n"
|
" " CTRL_OR_CMD "+Shift+n\n"
|
||||||
" collapse notification panel\n"
|
" collapse notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+c\n"
|
" " CTRL_OR_CMD "+c\n"
|
||||||
" copy device clipboard to computer\n"
|
" copy device clipboard to computer\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+v\n"
|
" " CTRL_OR_CMD "+v\n"
|
||||||
" paste computer clipboard to device\n"
|
" paste computer clipboard to device\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+Shift+v\n"
|
" " CTRL_OR_CMD "+Shift+v\n"
|
||||||
" copy computer clipboard to device\n"
|
" copy computer clipboard to device\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+i\n"
|
" " CTRL_OR_CMD "+i\n"
|
||||||
" enable/disable FPS counter (print frames/second in logs)\n"
|
" enable/disable FPS counter (print frames/second in logs)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Drag & drop APK file\n"
|
" Drag & drop APK file\n"
|
||||||
|
74
app/src/queue.h
Normal file
74
app/src/queue.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// generic intrusive FIFO queue
|
||||||
|
#ifndef QUEUE_H
|
||||||
|
#define QUEUE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
|
// To define a queue type of "struct foo":
|
||||||
|
// struct queue_foo QUEUE(struct foo);
|
||||||
|
#define QUEUE(TYPE) { \
|
||||||
|
TYPE *first; \
|
||||||
|
TYPE *last; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define queue_init(PQ) \
|
||||||
|
(void) ((PQ)->first = NULL)
|
||||||
|
|
||||||
|
#define queue_is_empty(PQ) \
|
||||||
|
!(PQ)->first
|
||||||
|
|
||||||
|
// NEXTFIELD is the field in the ITEM type used for intrusive linked-list
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// struct foo {
|
||||||
|
// int value;
|
||||||
|
// struct foo *next;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// // define the type "struct my_queue"
|
||||||
|
// struct my_queue QUEUE(struct foo);
|
||||||
|
//
|
||||||
|
// struct my_queue queue;
|
||||||
|
// queue_init(&queue);
|
||||||
|
//
|
||||||
|
// struct foo v1 = { .value = 42 };
|
||||||
|
// struct foo v2 = { .value = 27 };
|
||||||
|
//
|
||||||
|
// queue_push(&queue, next, v1);
|
||||||
|
// queue_push(&queue, next, v2);
|
||||||
|
//
|
||||||
|
// struct foo *foo;
|
||||||
|
// queue_take(&queue, next, &foo);
|
||||||
|
// assert(foo->value == 42);
|
||||||
|
// queue_take(&queue, next, &foo);
|
||||||
|
// assert(foo->value == 27);
|
||||||
|
// assert(queue_is_empty(&queue));
|
||||||
|
//
|
||||||
|
|
||||||
|
// push a new item into the queue
|
||||||
|
#define queue_push(PQ, NEXTFIELD, ITEM) \
|
||||||
|
(void) ({ \
|
||||||
|
(ITEM)->NEXTFIELD = NULL; \
|
||||||
|
if (queue_is_empty(PQ)) { \
|
||||||
|
(PQ)->first = (PQ)->last = (ITEM); \
|
||||||
|
} else { \
|
||||||
|
(PQ)->last->NEXTFIELD = (ITEM); \
|
||||||
|
(PQ)->last = (ITEM); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
// take the next item and remove it from the queue (the queue must not be empty)
|
||||||
|
// the result is stored in *(PITEM)
|
||||||
|
// (without typeof(), we could not store a local variable having the correct
|
||||||
|
// type so that we can "return" it)
|
||||||
|
#define queue_take(PQ, NEXTFIELD, PITEM) \
|
||||||
|
(void) ({ \
|
||||||
|
SDL_assert(!queue_is_empty(PQ)); \
|
||||||
|
*(PITEM) = (PQ)->first; \
|
||||||
|
(PQ)->first = (PQ)->first->NEXTFIELD; \
|
||||||
|
})
|
||||||
|
// no need to update (PQ)->last if the queue is left empty:
|
||||||
|
// (PQ)->last is undefined if !(PQ)->first anyway
|
||||||
|
|
||||||
|
#endif
|
@ -37,7 +37,6 @@ record_packet_new(const AVPacket *packet) {
|
|||||||
SDL_free(rec);
|
SDL_free(rec);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rec->next = NULL;
|
|
||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,60 +46,13 @@ record_packet_delete(struct record_packet *rec) {
|
|||||||
SDL_free(rec);
|
SDL_free(rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
recorder_queue_init(struct recorder_queue *queue) {
|
|
||||||
queue->first = NULL;
|
|
||||||
// queue->last is undefined if queue->first == NULL
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
recorder_queue_is_empty(struct recorder_queue *queue) {
|
|
||||||
return !queue->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
recorder_queue_push(struct recorder_queue *queue, const AVPacket *packet) {
|
|
||||||
struct record_packet *rec = record_packet_new(packet);
|
|
||||||
if (!rec) {
|
|
||||||
LOGC("Could not allocate record packet");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rec->next = NULL;
|
|
||||||
|
|
||||||
if (recorder_queue_is_empty(queue)) {
|
|
||||||
queue->first = queue->last = rec;
|
|
||||||
} else {
|
|
||||||
// chain rec after the (current) last packet
|
|
||||||
queue->last->next = rec;
|
|
||||||
// the last packet is now rec
|
|
||||||
queue->last = rec;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct record_packet *
|
|
||||||
recorder_queue_take(struct recorder_queue *queue) {
|
|
||||||
SDL_assert(!recorder_queue_is_empty(queue));
|
|
||||||
|
|
||||||
struct record_packet *rec = queue->first;
|
|
||||||
SDL_assert(rec);
|
|
||||||
|
|
||||||
queue->first = rec->next;
|
|
||||||
// no need to update queue->last if the queue is left empty:
|
|
||||||
// queue->last is undefined if queue->first == NULL
|
|
||||||
|
|
||||||
return rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
recorder_queue_clear(struct recorder_queue *queue) {
|
recorder_queue_clear(struct recorder_queue *queue) {
|
||||||
struct record_packet *rec = queue->first;
|
while (!queue_is_empty(queue)) {
|
||||||
while (rec) {
|
struct record_packet *rec;
|
||||||
struct record_packet *current = rec;
|
queue_take(queue, next, &rec);
|
||||||
rec = rec->next;
|
record_packet_delete(rec);
|
||||||
record_packet_delete(current);
|
|
||||||
}
|
}
|
||||||
queue->first = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -129,7 +81,7 @@ recorder_init(struct recorder *recorder,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder_queue_init(&recorder->queue);
|
queue_init(&recorder->queue);
|
||||||
recorder->stopped = false;
|
recorder->stopped = false;
|
||||||
recorder->failed = false;
|
recorder->failed = false;
|
||||||
recorder->format = format;
|
recorder->format = format;
|
||||||
@ -296,20 +248,20 @@ run_recorder(void *data) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
mutex_lock(recorder->mutex);
|
mutex_lock(recorder->mutex);
|
||||||
|
|
||||||
while (!recorder->stopped &&
|
while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
|
||||||
recorder_queue_is_empty(&recorder->queue)) {
|
|
||||||
cond_wait(recorder->queue_cond, recorder->mutex);
|
cond_wait(recorder->queue_cond, recorder->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if stopped is set, continue to process the remaining events (to
|
// if stopped is set, continue to process the remaining events (to
|
||||||
// finish the recording) before actually stopping
|
// finish the recording) before actually stopping
|
||||||
|
|
||||||
if (recorder->stopped && recorder_queue_is_empty(&recorder->queue)) {
|
if (recorder->stopped && queue_is_empty(&recorder->queue)) {
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct record_packet *rec = recorder_queue_take(&recorder->queue);
|
struct record_packet *rec;
|
||||||
|
queue_take(&recorder->queue, next, &rec);
|
||||||
|
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
|
|
||||||
@ -369,9 +321,15 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = recorder_queue_push(&recorder->queue, packet);
|
struct record_packet *rec = record_packet_new(packet);
|
||||||
|
if (!rec) {
|
||||||
|
LOGC("Could not allocate record packet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_push(&recorder->queue, next, rec);
|
||||||
cond_signal(recorder->queue_cond);
|
cond_signal(recorder->queue_cond);
|
||||||
|
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
return ok;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
enum recorder_format {
|
enum recorder_format {
|
||||||
RECORDER_FORMAT_MP4 = 1,
|
RECORDER_FORMAT_MP4 = 1,
|
||||||
@ -18,10 +19,7 @@ struct record_packet {
|
|||||||
struct record_packet *next;
|
struct record_packet *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct recorder_queue {
|
struct recorder_queue QUEUE(struct record_packet);
|
||||||
struct record_packet *first;
|
|
||||||
struct record_packet *last; // undefined if first is NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct recorder {
|
struct recorder {
|
||||||
char *filename;
|
char *filename;
|
||||||
|
38
app/tests/test_queue.c
Normal file
38
app/tests/test_queue.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <queue.h>
|
||||||
|
|
||||||
|
struct foo {
|
||||||
|
int value;
|
||||||
|
struct foo *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_queue(void) {
|
||||||
|
struct my_queue QUEUE(struct foo) queue;
|
||||||
|
queue_init(&queue);
|
||||||
|
|
||||||
|
assert(queue_is_empty(&queue));
|
||||||
|
|
||||||
|
struct foo v1 = { .value = 42 };
|
||||||
|
struct foo v2 = { .value = 27 };
|
||||||
|
|
||||||
|
queue_push(&queue, next, &v1);
|
||||||
|
queue_push(&queue, next, &v2);
|
||||||
|
|
||||||
|
struct foo *foo;
|
||||||
|
|
||||||
|
assert(!queue_is_empty(&queue));
|
||||||
|
queue_take(&queue, next, &foo);
|
||||||
|
assert(foo->value == 42);
|
||||||
|
|
||||||
|
assert(!queue_is_empty(&queue));
|
||||||
|
queue_take(&queue, next, &foo);
|
||||||
|
assert(foo->value == 27);
|
||||||
|
|
||||||
|
assert(queue_is_empty(&queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
test_queue();
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user