mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
269 lines
6.5 KiB
C
269 lines
6.5 KiB
C
|
/* GStreamer
|
||
|
* Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "key-handler.h"
|
||
|
|
||
|
#ifdef G_OS_WIN32
|
||
|
#include <windows.h>
|
||
|
|
||
|
typedef struct _Win32KeyHandler
|
||
|
{
|
||
|
GThread *thread;
|
||
|
HANDLE cancellable;
|
||
|
HANDLE console_handle;
|
||
|
GMutex lock;
|
||
|
gboolean closing;
|
||
|
|
||
|
KeyInputCallback callback;
|
||
|
gpointer user_data;
|
||
|
} Win32KeyHandler;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
KeyInputCallback callback;
|
||
|
gpointer user_data;
|
||
|
gchar value;
|
||
|
gboolean is_ascii;
|
||
|
} KeyInputCallbackData;
|
||
|
|
||
|
static Win32KeyHandler *_handler = NULL;
|
||
|
|
||
|
static gboolean
|
||
|
handler_source_func (KeyInputCallbackData * data)
|
||
|
{
|
||
|
data->callback (data->value, data->is_ascii, data->user_data);
|
||
|
|
||
|
return G_SOURCE_REMOVE;
|
||
|
}
|
||
|
|
||
|
static gpointer
|
||
|
handler_thread_func (Win32KeyHandler * handler)
|
||
|
{
|
||
|
HANDLE handles[2];
|
||
|
|
||
|
handles[0] = handler->cancellable;
|
||
|
handles[1] = handler->console_handle;
|
||
|
|
||
|
while (TRUE) {
|
||
|
DWORD ret = WaitForMultipleObjects (2, handles, FALSE, INFINITE);
|
||
|
INPUT_RECORD buffer;
|
||
|
DWORD num_read = 0;
|
||
|
KeyInputCallbackData *data;
|
||
|
|
||
|
if (ret == WAIT_FAILED) {
|
||
|
gst_printerrln ("Wait failed");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
g_mutex_lock (&handler->lock);
|
||
|
if (handler->closing) {
|
||
|
g_mutex_unlock (&handler->lock);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
g_mutex_unlock (&handler->lock);
|
||
|
|
||
|
if (!PeekConsoleInput (handler->console_handle, &buffer, 1, &num_read) ||
|
||
|
num_read != 1)
|
||
|
continue;
|
||
|
|
||
|
ReadConsoleInput (handler->console_handle, &buffer, 1, &num_read);
|
||
|
if (buffer.EventType != KEY_EVENT || !buffer.Event.KeyEvent.bKeyDown)
|
||
|
continue;
|
||
|
|
||
|
data = g_new0 (KeyInputCallbackData, 1);
|
||
|
data->callback = handler->callback;
|
||
|
data->user_data = handler->user_data;
|
||
|
|
||
|
switch (buffer.Event.KeyEvent.wVirtualKeyCode) {
|
||
|
case VK_UP:
|
||
|
data->value = (gchar) KB_ARROW_UP;
|
||
|
break;
|
||
|
case VK_DOWN:
|
||
|
data->value = (gchar) KB_ARROW_DOWN;
|
||
|
break;
|
||
|
case VK_LEFT:
|
||
|
data->value = (gchar) KB_ARROW_LEFT;
|
||
|
break;
|
||
|
case VK_RIGHT:
|
||
|
data->value = (gchar) KB_ARROW_RIGHT;
|
||
|
break;
|
||
|
default:
|
||
|
data->value = buffer.Event.KeyEvent.uChar.AsciiChar;
|
||
|
data->is_ascii = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
g_main_context_invoke_full (NULL,
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
(GSourceFunc) handler_source_func, data, (GDestroyNotify) g_free);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
set_key_handler (KeyInputCallback callback, gpointer user_data)
|
||
|
{
|
||
|
if (_handler || !callback)
|
||
|
return;
|
||
|
|
||
|
_handler = g_new0 (Win32KeyHandler, 1);
|
||
|
|
||
|
SECURITY_ATTRIBUTES attr;
|
||
|
attr.nLength = sizeof (SECURITY_ATTRIBUTES);
|
||
|
attr.lpSecurityDescriptor = NULL;
|
||
|
attr.bInheritHandle = FALSE;
|
||
|
|
||
|
_handler->cancellable = CreateEvent (&attr, TRUE, FALSE, NULL);
|
||
|
_handler->console_handle = GetStdHandle (STD_INPUT_HANDLE);
|
||
|
_handler->callback = callback;
|
||
|
_handler->user_data = user_data;
|
||
|
g_mutex_init (&_handler->lock);
|
||
|
_handler->thread =
|
||
|
g_thread_new ("key-handler", (GThreadFunc) handler_thread_func, _handler);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
unset_key_handler (void)
|
||
|
{
|
||
|
if (!_handler)
|
||
|
return;
|
||
|
|
||
|
g_mutex_lock (&_handler->lock);
|
||
|
_handler->closing = TRUE;
|
||
|
g_mutex_unlock (&_handler->lock);
|
||
|
|
||
|
SetEvent (_handler->cancellable);
|
||
|
g_thread_join (_handler->thread);
|
||
|
CloseHandle (_handler->cancellable);
|
||
|
g_mutex_clear (&_handler->lock);
|
||
|
|
||
|
g_clear_pointer (&_handler, g_free);
|
||
|
}
|
||
|
#else /* G_OS_WIN32 */
|
||
|
|
||
|
#include <termios.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
typedef struct _LinuxKeyHandler
|
||
|
{
|
||
|
gulong watch_id;
|
||
|
struct termios term_settings;
|
||
|
KeyInputCallback callback;
|
||
|
GSource *source;
|
||
|
gpointer user_data;
|
||
|
} LinuxKeyHandler;
|
||
|
|
||
|
static LinuxKeyHandler *_handler = NULL;
|
||
|
|
||
|
static gboolean
|
||
|
_handlerio_func (GIOChannel * channel,
|
||
|
GIOCondition condition, LinuxKeyHandler * handler)
|
||
|
{
|
||
|
if (condition & G_IO_IN) {
|
||
|
GIOStatus status;
|
||
|
gchar buf[16] = { 0, };
|
||
|
gsize read;
|
||
|
|
||
|
status =
|
||
|
g_io_channel_read_chars (channel, buf, sizeof (buf) - 1, &read, NULL);
|
||
|
if (status == G_IO_STATUS_ERROR) {
|
||
|
return G_SOURCE_REMOVE;
|
||
|
}
|
||
|
|
||
|
if (status == G_IO_STATUS_NORMAL) {
|
||
|
gchar value;
|
||
|
gboolean is_ascii = FALSE;
|
||
|
|
||
|
if (g_strcmp0 (buf, "\033[A") == 0) {
|
||
|
value = (gchar) KB_ARROW_UP;
|
||
|
} else if (g_strcmp0 (buf, "\033[B") == 0) {
|
||
|
value = (gchar) KB_ARROW_DOWN;
|
||
|
} else if (g_strcmp0 (buf, "\033[D") == 0) {
|
||
|
value = (gchar) KB_ARROW_LEFT;
|
||
|
} else if (g_strcmp0 (buf, "\033[C") == 0) {
|
||
|
value = (gchar) KB_ARROW_RIGHT;
|
||
|
} else {
|
||
|
value = buf[0];
|
||
|
is_ascii = TRUE;
|
||
|
}
|
||
|
|
||
|
handler->callback (value, is_ascii, handler->user_data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return G_SOURCE_CONTINUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
set_key_handler (KeyInputCallback callback, gpointer user_data)
|
||
|
{
|
||
|
struct termios new_settings;
|
||
|
struct termios old_settings;
|
||
|
GIOChannel *io_channel;
|
||
|
|
||
|
if (_handler || !callback)
|
||
|
return;
|
||
|
|
||
|
if (tcgetattr (STDIN_FILENO, &old_settings) != 0)
|
||
|
return;
|
||
|
|
||
|
new_settings = old_settings;
|
||
|
new_settings.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
||
|
new_settings.c_cc[VMIN] = 0;
|
||
|
new_settings.c_cc[VTIME] = 0;
|
||
|
|
||
|
if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &new_settings) != 0)
|
||
|
return;
|
||
|
|
||
|
setvbuf (stdin, NULL, _IONBF, 0);
|
||
|
|
||
|
_handler = g_new0 (LinuxKeyHandler, 1);
|
||
|
_handler->term_settings = old_settings;
|
||
|
_handler->callback = callback;
|
||
|
_handler->user_data = user_data;
|
||
|
|
||
|
io_channel = g_io_channel_unix_new (STDIN_FILENO);
|
||
|
|
||
|
_handler->source = g_io_create_watch (io_channel, G_IO_IN);
|
||
|
g_io_channel_unref (io_channel);
|
||
|
|
||
|
g_source_set_callback (_handler->source, (GSourceFunc) _handlerio_func,
|
||
|
_handler, NULL);
|
||
|
g_source_attach (_handler->source, NULL);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
unset_key_handler (void)
|
||
|
{
|
||
|
if (!_handler)
|
||
|
return;
|
||
|
|
||
|
if (_handler->source) {
|
||
|
g_source_destroy (_handler->source);
|
||
|
g_source_unref (_handler->source);
|
||
|
}
|
||
|
|
||
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, &_handler->term_settings);
|
||
|
setvbuf (stdin, NULL, _IOLBF, 0);
|
||
|
|
||
|
g_clear_pointer (&_handler, g_free);
|
||
|
}
|
||
|
#endif
|