mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-10 05:34:17 +00:00
Reorganize and prepare for SDL integration
This commit is contained in:
parent
ea459a0319
commit
ce64966843
18 changed files with 637 additions and 586 deletions
28
app/app.pro
28
app/app.pro
|
@ -29,29 +29,29 @@ macx {
|
|||
}
|
||||
unix:!macx {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += openssl
|
||||
PKGCONFIG += openssl sdl2
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
nvhttp.cpp \
|
||||
nvpairingmanager.cpp \
|
||||
identitymanager.cpp \
|
||||
popupmanager.cpp \
|
||||
streamwidget.cpp
|
||||
streaming/audio.c \
|
||||
streaming/input.c \
|
||||
gui/mainwindow.cpp \
|
||||
gui/popupmanager.cpp \
|
||||
http/identitymanager.cpp \
|
||||
http/nvhttp.cpp \
|
||||
http/nvpairingmanager.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
nvhttp.h \
|
||||
nvpairingmanager.h \
|
||||
identitymanager.h \
|
||||
utils.h \
|
||||
popupmanager.h \
|
||||
streamwidget.h
|
||||
gui/mainwindow.h \
|
||||
gui/popupmanager.h \
|
||||
http/identitymanager.h \
|
||||
http/nvhttp.h \
|
||||
http/nvpairingmanager.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
gui/mainwindow.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "mainwindow.h"
|
||||
#include "gui/mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "popupmanager.h"
|
||||
#include "identitymanager.h"
|
||||
#include "nvpairingmanager.h"
|
||||
#include "nvhttp.h"
|
||||
#include "gui/popupmanager.h"
|
||||
#include "http/identitymanager.h"
|
||||
#include "http/nvpairingmanager.h"
|
||||
#include "http/nvhttp.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <identitymanager.h>
|
||||
#include <nvhttp.h>
|
||||
#include "http/identitymanager.h"
|
||||
#include "http/nvhttp.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/x509.h>
|
|
@ -1,4 +1,4 @@
|
|||
#include "mainwindow.h"
|
||||
#include "gui/mainwindow.h"
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
|
130
app/streaming/audio.c
Normal file
130
app/streaming/audio.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include <Limelight.h>
|
||||
#include <opus_multistream.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#define MAX_CHANNELS 6
|
||||
#define SAMPLES_PER_FRAME 240
|
||||
|
||||
static SDL_AudioDeviceID g_AudioDevice;
|
||||
static OpusMSDecoder* g_OpusDecoder;
|
||||
static short g_OpusDecodeBuffer[MAX_CHANNELS * SAMPLES_PER_FRAME];
|
||||
static OPUS_MULTISTREAM_CONFIGURATION g_OpusConfig;
|
||||
|
||||
int SdlDetermineAudioConfiguration(void)
|
||||
{
|
||||
SDL_AudioSpec want, have;
|
||||
SDL_AudioDeviceID dev;
|
||||
|
||||
SDL_zero(want);
|
||||
want.freq = 48000;
|
||||
want.format = AUDIO_S16;
|
||||
want.channels = 6;
|
||||
want.samples = 1024;
|
||||
|
||||
// Try to open for 5.1 surround sound, but allow SDL to tell us that's
|
||||
// not available.
|
||||
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
|
||||
if (dev == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to open audio device");
|
||||
// We'll probably have issues during audio stream init, but we'll
|
||||
// try anyway
|
||||
return AUDIO_CONFIGURATION_STEREO;
|
||||
}
|
||||
|
||||
SDL_CloseAudioDevice(dev);
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Audio device has %d channels", have.channels);
|
||||
|
||||
if (have.channels > 2) {
|
||||
// We don't support quadraphonic or 7.1 surround, but SDL
|
||||
// should be able to downmix or upmix better from 5.1 than
|
||||
// from stereo, so use 5.1 in non-stereo cases.
|
||||
return AUDIO_CONFIGURATION_51_SURROUND;
|
||||
}
|
||||
else {
|
||||
return AUDIO_CONFIGURATION_STEREO;
|
||||
}
|
||||
}
|
||||
|
||||
int SdlAudioInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* arContext, int arFlags)
|
||||
{
|
||||
SDL_AudioSpec want, have;
|
||||
int error;
|
||||
|
||||
SDL_zero(want);
|
||||
want.freq = opusConfig->sampleRate;
|
||||
want.format = AUDIO_S16;
|
||||
want.channels = opusConfig->channelCount;
|
||||
want.samples = 1024;
|
||||
|
||||
g_AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
||||
if (g_AudioDevice == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to open audio device: %s",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_OpusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
opusConfig->streams,
|
||||
opusConfig->coupledStreams,
|
||||
opusConfig->mapping,
|
||||
&error);
|
||||
if (g_OpusDecoder == NULL) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to create decoder: %d",
|
||||
error);
|
||||
SDL_CloseAudioDevice(g_AudioDevice);
|
||||
g_AudioDevice = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
SDL_memcpy(&g_OpusConfig, opusConfig, sizeof(g_OpusConfig));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SdlAudioStart(void)
|
||||
{
|
||||
// Unpause the audio device
|
||||
SDL_PauseAudioDevice(g_AudioDevice, 0);
|
||||
}
|
||||
|
||||
void SdlAudioStop(void)
|
||||
{
|
||||
// Pause the audio device
|
||||
SDL_PauseAudioDevice(g_AudioDevice, 1);
|
||||
}
|
||||
|
||||
void SdlAudioCleanup(void)
|
||||
{
|
||||
SDL_CloseAudioDevice(g_AudioDevice);
|
||||
g_AudioDevice = 0;
|
||||
|
||||
opus_multistream_decoder_destroy(g_OpusDecoder);
|
||||
g_OpusDecoder = NULL;
|
||||
}
|
||||
|
||||
void SdlAudioDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||
{
|
||||
int samplesDecoded;
|
||||
|
||||
samplesDecoded = opus_multistream_decode(g_OpusDecoder,
|
||||
(unsigned char*)sampleData,
|
||||
sampleLength,
|
||||
g_OpusDecodeBuffer,
|
||||
SAMPLES_PER_FRAME,
|
||||
0);
|
||||
if (samplesDecoded > 0) {
|
||||
if (SDL_QueueAudio(g_AudioDevice,
|
||||
g_OpusDecodeBuffer,
|
||||
sizeof(short) * samplesDecoded * g_OpusConfig.channelCount) < 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to queue audio sample: %s",
|
||||
SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
484
app/streaming/input.c
Normal file
484
app/streaming/input.c
Normal file
|
@ -0,0 +1,484 @@
|
|||
#include <Limelight.h>
|
||||
#include <SDL.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define VK_0 0x30
|
||||
#define VK_A 0x41
|
||||
|
||||
// These are real Windows VK_* codes
|
||||
#ifndef VK_F1
|
||||
#define VK_F1 0x70
|
||||
#define VK_F13 0x7C
|
||||
#define VK_NUMPAD1 0x61
|
||||
#endif
|
||||
|
||||
typedef struct _GAMEPAD_STATE {
|
||||
SDL_GameController* controller;
|
||||
SDL_JoystickID jsId;
|
||||
short index;
|
||||
|
||||
short buttons;
|
||||
short lsX, lsY;
|
||||
short rsX, rsY;
|
||||
unsigned char lt, rt;
|
||||
} GAMEPAD_STATE, *PGAMEPAD_STATE;
|
||||
|
||||
#define MAX_GAMEPADS 4
|
||||
GAMEPAD_STATE g_GamepadState[MAX_GAMEPADS];
|
||||
unsigned short g_GamepadMask;
|
||||
bool g_MultiController;
|
||||
|
||||
const short k_ButtonMap[] = {
|
||||
A_FLAG, B_FLAG, X_FLAG, Y_FLAG,
|
||||
BACK_FLAG, SPECIAL_FLAG, PLAY_FLAG,
|
||||
LS_CLK_FLAG, RS_CLK_FLAG,
|
||||
LB_FLAG, RB_FLAG,
|
||||
UP_FLAG, DOWN_FLAG, LEFT_FLAG, RIGHT_FLAG
|
||||
};
|
||||
|
||||
void SdlHandleKeyEvent(SDL_KeyboardEvent* event)
|
||||
{
|
||||
short keyCode;
|
||||
char modifiers;
|
||||
|
||||
// Set modifier flags
|
||||
modifiers = 0;
|
||||
if (event->keysym.mod & KMOD_CTRL) {
|
||||
modifiers |= MODIFIER_CTRL;
|
||||
}
|
||||
if (event->keysym.mod & KMOD_ALT) {
|
||||
modifiers |= MODIFIER_ALT;
|
||||
}
|
||||
if (event->keysym.mod & KMOD_SHIFT) {
|
||||
modifiers |= MODIFIER_SHIFT;
|
||||
}
|
||||
|
||||
// Ignore repeats
|
||||
if (event->repeat != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set keycode
|
||||
if (event->keysym.sym >= SDLK_0 && event->keysym.sym <= SDLK_9) {
|
||||
keyCode = (event->keysym.sym - SDLK_0) + VK_0;
|
||||
}
|
||||
else if (event->keysym.sym >= SDLK_a && event->keysym.sym <= SDLK_z) {
|
||||
keyCode = (event->keysym.sym - SDLK_a) + VK_A;
|
||||
}
|
||||
else if (event->keysym.sym >= SDLK_F1 && event->keysym.sym <= SDLK_F12) {
|
||||
keyCode = (event->keysym.sym - SDLK_F1) + VK_F1;
|
||||
}
|
||||
else if (event->keysym.sym >= SDLK_F13 && event->keysym.sym <= SDLK_F24) {
|
||||
keyCode = (event->keysym.sym - SDLK_F13) + VK_F13;
|
||||
}
|
||||
else if (event->keysym.sym >= SDLK_KP_1 && event->keysym.sym <= SDLK_KP_9) {
|
||||
// SDL defines SDLK_KP_0 > SDLK_KP_9, so we need to handle that manually
|
||||
keyCode = (event->keysym.sym - SDLK_KP_1) + VK_NUMPAD1;
|
||||
}
|
||||
else {
|
||||
switch (event->keysym.sym) {
|
||||
case SDLK_BACKSPACE:
|
||||
keyCode = 0x08;
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
keyCode = 0x09;
|
||||
break;
|
||||
case SDLK_CLEAR:
|
||||
keyCode = 0x0C;
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
keyCode = 0x0D;
|
||||
break;
|
||||
case SDLK_PAUSE:
|
||||
keyCode = 0x13;
|
||||
break;
|
||||
case SDLK_CAPSLOCK:
|
||||
keyCode = 0x14;
|
||||
break;
|
||||
case SDLK_ESCAPE:
|
||||
keyCode = 0x1B;
|
||||
break;
|
||||
case SDLK_SPACE:
|
||||
keyCode = 0x20;
|
||||
break;
|
||||
case SDLK_PAGEUP:
|
||||
keyCode = 0x21;
|
||||
break;
|
||||
case SDLK_PAGEDOWN:
|
||||
keyCode = 0x22;
|
||||
break;
|
||||
case SDLK_END:
|
||||
keyCode = 0x23;
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
keyCode = 0x24;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
keyCode = 0x25;
|
||||
break;
|
||||
case SDLK_UP:
|
||||
keyCode = 0x26;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
keyCode = 0x27;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
keyCode = 0x28;
|
||||
break;
|
||||
case SDLK_SELECT:
|
||||
keyCode = 0x29;
|
||||
break;
|
||||
case SDLK_EXECUTE:
|
||||
keyCode = 0x2B;
|
||||
break;
|
||||
case SDLK_PRINTSCREEN:
|
||||
keyCode = 0x2C;
|
||||
break;
|
||||
case SDLK_INSERT:
|
||||
keyCode = 0x2D;
|
||||
break;
|
||||
case SDLK_DELETE:
|
||||
keyCode = 0x2E;
|
||||
break;
|
||||
case SDLK_HELP:
|
||||
keyCode = 0x2F;
|
||||
break;
|
||||
case SDLK_KP_0:
|
||||
// See comment above about why we only handle KP_0 here
|
||||
keyCode = 0x60;
|
||||
break;
|
||||
case SDLK_KP_MULTIPLY:
|
||||
keyCode = 0x6A;
|
||||
break;
|
||||
case SDLK_KP_PLUS:
|
||||
keyCode = 0x6B;
|
||||
break;
|
||||
case SDLK_KP_COMMA:
|
||||
keyCode = 0x6C;
|
||||
break;
|
||||
case SDLK_KP_MINUS:
|
||||
keyCode = 0x6D;
|
||||
break;
|
||||
case SDLK_KP_DECIMAL:
|
||||
keyCode = 0x6E;
|
||||
break;
|
||||
case SDLK_KP_DIVIDE:
|
||||
keyCode = 0x6F;
|
||||
break;
|
||||
case SDLK_NUMLOCKCLEAR:
|
||||
keyCode = 0x90;
|
||||
break;
|
||||
case SDLK_SCROLLLOCK:
|
||||
keyCode = 0x91;
|
||||
break;
|
||||
case SDLK_LSHIFT:
|
||||
keyCode = 0xA0;
|
||||
break;
|
||||
case SDLK_RSHIFT:
|
||||
keyCode = 0xA1;
|
||||
break;
|
||||
case SDLK_LCTRL:
|
||||
keyCode = 0xA2;
|
||||
break;
|
||||
case SDLK_RCTRL:
|
||||
keyCode = 0xA3;
|
||||
break;
|
||||
case SDLK_LALT:
|
||||
keyCode = 0xA4;
|
||||
break;
|
||||
case SDLK_RALT:
|
||||
keyCode = 0xA5;
|
||||
break;
|
||||
case SDLK_AC_BACK:
|
||||
keyCode = 0xA6;
|
||||
break;
|
||||
case SDLK_AC_FORWARD:
|
||||
keyCode = 0xA7;
|
||||
break;
|
||||
case SDLK_AC_REFRESH:
|
||||
keyCode = 0xA8;
|
||||
break;
|
||||
case SDLK_AC_STOP:
|
||||
keyCode = 0xA9;
|
||||
break;
|
||||
case SDLK_AC_SEARCH:
|
||||
keyCode = 0xAA;
|
||||
break;
|
||||
case SDLK_AC_BOOKMARKS:
|
||||
keyCode = 0xAB;
|
||||
break;
|
||||
case SDLK_AC_HOME:
|
||||
keyCode = 0xAC;
|
||||
break;
|
||||
case SDLK_SEMICOLON:
|
||||
keyCode = 0xBA;
|
||||
break;
|
||||
case SDLK_PLUS:
|
||||
keyCode = 0xBB;
|
||||
break;
|
||||
case SDLK_COMMA:
|
||||
keyCode = 0xBC;
|
||||
break;
|
||||
case SDLK_MINUS:
|
||||
keyCode = 0xBD;
|
||||
break;
|
||||
case SDLK_PERIOD:
|
||||
keyCode = 0xBE;
|
||||
break;
|
||||
case SDLK_SLASH:
|
||||
keyCode = 0xBF;
|
||||
break;
|
||||
case SDLK_BACKQUOTE:
|
||||
keyCode = 0xC0;
|
||||
break;
|
||||
case SDLK_LEFTBRACKET:
|
||||
keyCode = 0xDB;
|
||||
break;
|
||||
case SDLK_BACKSLASH:
|
||||
keyCode = 0xDC;
|
||||
break;
|
||||
case SDLK_RIGHTBRACKET:
|
||||
keyCode = 0xDD;
|
||||
break;
|
||||
case SDLK_QUOTE:
|
||||
keyCode = 0xDE;
|
||||
break;
|
||||
default:
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unhandled button event: %d",
|
||||
event->keysym.sym);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LiSendKeyboardEvent(keyCode,
|
||||
event->state == SDL_PRESSED ?
|
||||
KEY_ACTION_DOWN : KEY_ACTION_UP,
|
||||
modifiers);
|
||||
}
|
||||
|
||||
void SdlHandleMouseButtonEvent(SDL_MouseButtonEvent* event)
|
||||
{
|
||||
int button;
|
||||
|
||||
switch (event->button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
button = BUTTON_LEFT;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
button = BUTTON_MIDDLE;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
button = BUTTON_RIGHT;
|
||||
break;
|
||||
case SDL_BUTTON_X1:
|
||||
case SDL_BUTTON_X2:
|
||||
// Unsupported by GameStream
|
||||
return;
|
||||
default:
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unhandled button event: %d",
|
||||
event->button);
|
||||
return;
|
||||
}
|
||||
|
||||
LiSendMouseButtonEvent(event->state == SDL_PRESSED ?
|
||||
BUTTON_ACTION_PRESS :
|
||||
BUTTON_ACTION_RELEASE,
|
||||
button);
|
||||
}
|
||||
|
||||
void SdlHandleMouseMotionEvent(SDL_MouseMotionEvent* event)
|
||||
{
|
||||
if (event->xrel != 0 || event->yrel != 0) {
|
||||
LiSendMouseMoveEvent((unsigned short)event->xrel,
|
||||
(unsigned short)event->yrel);
|
||||
}
|
||||
}
|
||||
|
||||
void SdlHandleMouseWheelEvent(SDL_MouseWheelEvent* event)
|
||||
{
|
||||
if (event->y != 0) {
|
||||
LiSendScrollEvent((signed char)event->y);
|
||||
}
|
||||
}
|
||||
|
||||
static PGAMEPAD_STATE FindStateForGamepad(SDL_JoystickID id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_GAMEPADS; i++) {
|
||||
if (g_GamepadState[i].jsId == id) {
|
||||
SDL_assert(!g_MultiController || g_GamepadState[i].index == i);
|
||||
return &g_GamepadState[i];
|
||||
}
|
||||
}
|
||||
|
||||
// This should only happen with > 4 gamepads
|
||||
SDL_assert(SDL_NumJoysticks() > 4);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SendGamepadState(PGAMEPAD_STATE state)
|
||||
{
|
||||
SDL_assert(g_GamepadMask == 0x1 || g_MultiController);
|
||||
LiSendMultiControllerEvent(state->index,
|
||||
g_GamepadMask,
|
||||
state->buttons,
|
||||
state->lt,
|
||||
state->rt,
|
||||
state->lsX,
|
||||
state->lsY,
|
||||
state->rsX,
|
||||
state->rsY);
|
||||
}
|
||||
|
||||
void SdlHandleControllerAxisEvent(SDL_ControllerAxisEvent* event)
|
||||
{
|
||||
PGAMEPAD_STATE state = FindStateForGamepad(event->which);
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->axis)
|
||||
{
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
state->lsX = event->value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
state->lsY = event->value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
state->rsX = event->value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
state->rsY = event->value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
state->lt = (unsigned char)((event->value + 32768UL) * 255 / 65535);
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
state->rt = (unsigned char)((event->value + 32768UL) * 255 / 65535);
|
||||
break;
|
||||
default:
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unhandled controller axis: %d",
|
||||
event->axis);
|
||||
return;
|
||||
}
|
||||
|
||||
SendGamepadState(state);
|
||||
}
|
||||
|
||||
void SdlHandleControllerButtonEvent(SDL_ControllerButtonEvent* event)
|
||||
{
|
||||
PGAMEPAD_STATE state = FindStateForGamepad(event->which);
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->state == SDL_PRESSED) {
|
||||
state->buttons |= k_ButtonMap[event->button];
|
||||
}
|
||||
else {
|
||||
state->buttons &= ~k_ButtonMap[event->button];
|
||||
}
|
||||
|
||||
SendGamepadState(state);
|
||||
}
|
||||
|
||||
void SdlHandleControllerDeviceEvent(SDL_ControllerDeviceEvent* event)
|
||||
{
|
||||
PGAMEPAD_STATE state;
|
||||
|
||||
if (event->type == SDL_CONTROLLERDEVICEADDED) {
|
||||
int i;
|
||||
const char* name;
|
||||
|
||||
for (i = 0; i < MAX_GAMEPADS; i++) {
|
||||
if (g_GamepadState[i].controller == NULL) {
|
||||
// Found an empty slot
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_GAMEPADS) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"No open gamepad slots found!");
|
||||
return;
|
||||
}
|
||||
|
||||
state = &g_GamepadState[i];
|
||||
if (g_MultiController) {
|
||||
state->index = i;
|
||||
}
|
||||
else {
|
||||
// Always player 1 in single controller mode
|
||||
state->index = 0;
|
||||
}
|
||||
state->controller = SDL_GameControllerOpen(event->which);
|
||||
if (state->controller == NULL) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to open gamepad: %s",
|
||||
SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller));
|
||||
|
||||
name = SDL_GameControllerName(state->controller);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Gamepad %d (player %d) is: %s",
|
||||
i,
|
||||
state->index,
|
||||
name != NULL ? name : "<null>");
|
||||
|
||||
// Add this gamepad to the gamepad mask
|
||||
if (g_MultiController) {
|
||||
SDL_assert(!(g_GamepadMask & (1 << state->index)));
|
||||
g_GamepadMask |= (1 << state->index);
|
||||
}
|
||||
else {
|
||||
SDL_assert(g_GamepadMask == 0x1);
|
||||
}
|
||||
|
||||
// Send an empty event to tell the PC we've arrived
|
||||
SendGamepadState(state);
|
||||
}
|
||||
else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
state = FindStateForGamepad(event->which);
|
||||
if (state != NULL) {
|
||||
SDL_GameControllerClose(state->controller);
|
||||
|
||||
// Remove this from the gamepad mask in MC-mode
|
||||
if (g_MultiController) {
|
||||
SDL_assert(g_GamepadMask & (1 << state->index));
|
||||
g_GamepadMask &= ~(1 << state->index);
|
||||
}
|
||||
else {
|
||||
SDL_assert(g_GamepadMask == 0x1);
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Gamepad %d is gone",
|
||||
state->index);
|
||||
|
||||
// Send a final event to let the PC know this gamepad is gone
|
||||
LiSendMultiControllerEvent(state->index, g_GamepadMask,
|
||||
0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
// Clear all remaining state from this slot
|
||||
SDL_memset(state, 0, sizeof(*state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdlInitializeGamepad(bool multiController)
|
||||
{
|
||||
g_MultiController = multiController;
|
||||
if (!g_MultiController) {
|
||||
// Player 1 is always present in non-MC mode
|
||||
g_GamepadMask = 0x01;
|
||||
}
|
||||
}
|
|
@ -1,478 +0,0 @@
|
|||
#include "streamwidget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QGamepadManager>
|
||||
|
||||
#include "Limelight.h"
|
||||
|
||||
#define VK_0 0x30
|
||||
#define VK_A 0x41
|
||||
|
||||
// These are real Windows VK_* codes
|
||||
#ifndef VK_F1
|
||||
#define VK_F1 0x70
|
||||
#define VK_NUMPAD0 0x60
|
||||
#endif
|
||||
|
||||
StreamWidget::StreamWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_LastMouseX(0),
|
||||
m_LastMouseY(0),
|
||||
m_ScrollDeltaX(0),
|
||||
m_ActiveGamepadMask(0)
|
||||
{
|
||||
// Ensure we get mouse move events even if no buttons are down
|
||||
setMouseTracking(true);
|
||||
|
||||
// Register for QGamepadManager's signals
|
||||
QGamepadManager* gpm = QGamepadManager::instance();
|
||||
connect(gpm, &QGamepadManager::gamepadConnected, this, &StreamWidget::gamepadConnected);
|
||||
connect(gpm, &QGamepadManager::gamepadDisconnected, this, &StreamWidget::gamepadDisconnected);
|
||||
connect(gpm, &QGamepadManager::gamepadButtonPressEvent, this, &StreamWidget::gamepadButtonPressEvent);
|
||||
connect(gpm, &QGamepadManager::gamepadButtonReleaseEvent, this, &StreamWidget::gamepadButtonReleaseEvent);
|
||||
connect(gpm, &QGamepadManager::gamepadAxisEvent, this, &StreamWidget::gamepadAxisEvent);
|
||||
|
||||
// We won't be invoked for existing gamepads, so "connect" them now
|
||||
for (int deviceId : gpm->connectedGamepads())
|
||||
{
|
||||
gamepadConnected(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
int currentX = event->globalX();
|
||||
int currentY = event->globalY();
|
||||
|
||||
if (m_LastMouseX != 0 && m_LastMouseY != 0)
|
||||
{
|
||||
LiSendMouseMoveEvent(m_LastMouseX - currentX,
|
||||
m_LastMouseY - currentY);
|
||||
}
|
||||
|
||||
m_LastMouseX = currentX;
|
||||
m_LastMouseY = currentY;
|
||||
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::handleMouseButtonEvent(QMouseEvent *event)
|
||||
{
|
||||
Qt::MouseButton button = event->button();
|
||||
int buttonCode;
|
||||
|
||||
Q_ASSERT(button != Qt::MouseButton::NoButton);
|
||||
Q_ASSERT(event->type() == QEvent::Type::MouseButtonPress ||
|
||||
event->type() == QEvent::Type::MouseButtonRelease);
|
||||
|
||||
// Accept all mouse button events
|
||||
event->accept();
|
||||
|
||||
switch (button)
|
||||
{
|
||||
case Qt::MouseButton::LeftButton:
|
||||
buttonCode = BUTTON_LEFT;
|
||||
break;
|
||||
case Qt::MouseButton::MiddleButton:
|
||||
buttonCode = BUTTON_MIDDLE;
|
||||
break;
|
||||
case Qt::MouseButton::RightButton:
|
||||
buttonCode = BUTTON_RIGHT;
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unhandled mouse button: " << button;
|
||||
return;
|
||||
}
|
||||
|
||||
LiSendMouseButtonEvent((event->type() == QEvent::Type::MouseButtonPress) ?
|
||||
BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE,
|
||||
buttonCode);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
handleMouseButtonEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
handleMouseButtonEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
QPoint scrollDelta = event->angleDelta();
|
||||
|
||||
if (!scrollDelta.isNull())
|
||||
{
|
||||
m_ScrollDeltaX += scrollDelta.x();
|
||||
}
|
||||
|
||||
// See if we've accumulated enough to send
|
||||
if (m_ScrollDeltaX / 120 != 0)
|
||||
{
|
||||
// Send the scroll event
|
||||
LiSendScrollEvent(m_ScrollDeltaX / 120);
|
||||
|
||||
// Subtract the portion of the total scroll delta we're "consuming"
|
||||
m_ScrollDeltaX -= (m_ScrollDeltaX / 120) * 120;
|
||||
}
|
||||
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::handleKeyEvent(QKeyEvent *event)
|
||||
{
|
||||
// Accept all events
|
||||
event->accept();
|
||||
|
||||
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
|
||||
char modifierFlags = 0;
|
||||
char keyAction = event->type() == QEvent::KeyPress ?
|
||||
KEY_ACTION_DOWN : KEY_ACTION_UP;
|
||||
|
||||
// Don't send auto-repeating events
|
||||
if (keyAction == KEY_ACTION_DOWN && event->isAutoRepeat())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (modifiers.testFlag(Qt::KeyboardModifier::AltModifier))
|
||||
{
|
||||
modifierFlags |= MODIFIER_ALT;
|
||||
}
|
||||
if (modifiers.testFlag(Qt::KeyboardModifier::ControlModifier))
|
||||
{
|
||||
modifierFlags |= MODIFIER_CTRL;
|
||||
}
|
||||
if (modifiers.testFlag(Qt::KeyboardModifier::ShiftModifier))
|
||||
{
|
||||
modifierFlags |= MODIFIER_SHIFT;
|
||||
}
|
||||
|
||||
short keyCode;
|
||||
Qt::Key key = static_cast<Qt::Key>(event->key());
|
||||
|
||||
if (key >= Qt::Key::Key_0 && key <= Qt::Key::Key_9)
|
||||
{
|
||||
// In Qt, there's no separate key code for numpad button.
|
||||
// Numpad is indicated in the modifier flags.
|
||||
keyCode = (key - Qt::Key::Key_0) +
|
||||
(event->modifiers().testFlag(Qt::KeyboardModifier::KeypadModifier) ?
|
||||
VK_NUMPAD0 : VK_0);
|
||||
}
|
||||
else if (key >= Qt::Key::Key_A && key <= Qt::Key::Key_Z)
|
||||
{
|
||||
keyCode = (key - Qt::Key::Key_A) + VK_A;
|
||||
}
|
||||
else if (key >= Qt::Key::Key_F1 && key <= Qt::Key::Key_F12)
|
||||
{
|
||||
keyCode = (key - Qt::Key::Key_F1) + VK_F1;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Qt::Key::Key_Alt:
|
||||
// TODO: Tell left and right apart
|
||||
keyCode = 0xA4; // 0xA5 (right)
|
||||
break;
|
||||
case Qt::Key::Key_Backslash:
|
||||
keyCode = 0xDC;
|
||||
break;
|
||||
case Qt::Key::Key_CapsLock:
|
||||
keyCode = 0x14;
|
||||
break;
|
||||
case Qt::Key::Key_Clear:
|
||||
keyCode = 0x0C;
|
||||
break;
|
||||
case Qt::Key::Key_Comma:
|
||||
keyCode = 0xBC;
|
||||
break;
|
||||
case Qt::Key::Key_Control:
|
||||
// TODO: left and right
|
||||
keyCode = 0xA2; // 0xA3 (right)
|
||||
break;
|
||||
case Qt::Key::Key_Backspace:
|
||||
keyCode = 0x08;
|
||||
break;
|
||||
case Qt::Key::Key_Return:
|
||||
keyCode = 0x0D;
|
||||
break;
|
||||
case Qt::Key::Key_Equal:
|
||||
keyCode = 0xBB;
|
||||
break;
|
||||
case Qt::Key::Key_Escape:
|
||||
keyCode = 0x1B;
|
||||
break;
|
||||
case Qt::Key::Key_Delete:
|
||||
keyCode = 0x2E;
|
||||
break;
|
||||
case Qt::Key::Key_Insert:
|
||||
keyCode = 0x2D;
|
||||
break;
|
||||
case Qt::Key::Key_BracketLeft:
|
||||
keyCode = 0xDB;
|
||||
break;
|
||||
case Qt::Key::Key_BracketRight:
|
||||
keyCode = 0xDD;
|
||||
break;
|
||||
case Qt::Key::Key_Minus:
|
||||
keyCode = 0xBD;
|
||||
break;
|
||||
case Qt::Key::Key_End:
|
||||
keyCode = 0x23;
|
||||
break;
|
||||
case Qt::Key::Key_Home:
|
||||
keyCode = 0x24;
|
||||
break;
|
||||
case Qt::Key::Key_NumLock:
|
||||
keyCode = 0x90;
|
||||
break;
|
||||
case Qt::Key::Key_PageDown:
|
||||
keyCode = 0x22;
|
||||
break;
|
||||
case Qt::Key::Key_PageUp:
|
||||
keyCode = 0x21;
|
||||
break;
|
||||
case Qt::Key::Key_Period:
|
||||
keyCode = 0xBE;
|
||||
break;
|
||||
case Qt::Key::Key_ScrollLock:
|
||||
keyCode = 0x91;
|
||||
break;
|
||||
case Qt::Key::Key_Semicolon:
|
||||
keyCode = 0xBA;
|
||||
break;
|
||||
case Qt::Key::Key_Shift:
|
||||
// TODO: left vs right
|
||||
keyCode = 0xA0; // A1 (right)
|
||||
break;
|
||||
case Qt::Key::Key_Slash:
|
||||
keyCode = 0xBF;
|
||||
break;
|
||||
case Qt::Key::Key_Space:
|
||||
keyCode = 0x20;
|
||||
break;
|
||||
case Qt::Key::Key_SysReq:
|
||||
keyCode = 0x9A;
|
||||
break;
|
||||
case Qt::Key::Key_Tab:
|
||||
keyCode = 0x09;
|
||||
break;
|
||||
case Qt::Key::Key_Left:
|
||||
keyCode = 0x25;
|
||||
break;
|
||||
case Qt::Key::Key_Right:
|
||||
keyCode = 0x27;
|
||||
break;
|
||||
case Qt::Key::Key_Up:
|
||||
keyCode = 0x26;
|
||||
break;
|
||||
case Qt::Key::Key_Down:
|
||||
keyCode = 0x28;
|
||||
break;
|
||||
case Qt::Key::Key_QuoteLeft:
|
||||
keyCode = 0xC0;
|
||||
break;
|
||||
case Qt::Key::Key_Apostrophe:
|
||||
keyCode = 0xDE;
|
||||
break;
|
||||
case Qt::Key::Key_Pause:
|
||||
keyCode = 0x13;
|
||||
break;
|
||||
// FIXME: Numpad keys
|
||||
default:
|
||||
qWarning() << "Unhandled key: " << key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LiSendKeyboardEvent(0x80 | keyCode, keyAction, modifierFlags);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
handleKeyEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
handleKeyEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::gamepadAxisEvent(int deviceId, QGamepadManager::GamepadAxis axis, double value)
|
||||
{
|
||||
GamepadState* gamepad = m_Gamepads.value(deviceId, nullptr);
|
||||
Q_ASSERT(gamepad != nullptr);
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case QGamepadManager::GamepadAxis::AxisLeftX:
|
||||
gamepad->LeftStickX = value * 0x7FFF;
|
||||
break;
|
||||
case QGamepadManager::GamepadAxis::AxisLeftY:
|
||||
gamepad->LeftStickY = value * 0x7FFF;
|
||||
break;
|
||||
case QGamepadManager::GamepadAxis::AxisRightX:
|
||||
gamepad->RightStickX = value * 0x7FFF;
|
||||
break;
|
||||
case QGamepadManager::GamepadAxis::AxisRightY:
|
||||
gamepad->RightStickY = value * 0x7FFF;
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unhandled axis: " << axis;
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::handleGamepadButtonEvent(int deviceId, QGamepadManager::GamepadButton button, double value)
|
||||
{
|
||||
GamepadState* gamepad = m_Gamepads.value(deviceId, nullptr);
|
||||
Q_ASSERT(gamepad != nullptr);
|
||||
|
||||
|
||||
// Triggers are special cases
|
||||
if (button == QGamepadManager::GamepadButton::ButtonL2)
|
||||
{
|
||||
gamepad->LeftTrigger = value * 0xFF;
|
||||
}
|
||||
else if (button == QGamepadManager::GamepadButton::ButtonR2)
|
||||
{
|
||||
gamepad->RightTrigger = value * 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
short buttonFlag;
|
||||
switch (button)
|
||||
{
|
||||
case QGamepadManager::GamepadButton::ButtonA:
|
||||
buttonFlag = A_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonB:
|
||||
buttonFlag = B_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonX:
|
||||
buttonFlag = X_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonY:
|
||||
buttonFlag = Y_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonSelect:
|
||||
buttonFlag = BACK_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonStart:
|
||||
buttonFlag = PLAY_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonGuide:
|
||||
buttonFlag = SPECIAL_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonUp:
|
||||
buttonFlag = UP_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonDown:
|
||||
buttonFlag = DOWN_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonLeft:
|
||||
buttonFlag = LEFT_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonRight:
|
||||
buttonFlag = RIGHT_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonL3:
|
||||
buttonFlag = LS_CLK_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonR3:
|
||||
buttonFlag = RS_CLK_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonL1:
|
||||
buttonFlag = LB_FLAG;
|
||||
break;
|
||||
case QGamepadManager::GamepadButton::ButtonR1:
|
||||
buttonFlag = RB_FLAG;
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unhandled button: " << button;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
gamepad->ButtonFlags |= buttonFlag;
|
||||
}
|
||||
else
|
||||
{
|
||||
gamepad->ButtonFlags &= ~buttonFlag;
|
||||
}
|
||||
}
|
||||
|
||||
LiSendMultiControllerEvent(gamepad->Index,
|
||||
m_ActiveGamepadMask,
|
||||
gamepad->ButtonFlags,
|
||||
gamepad->LeftTrigger,
|
||||
gamepad->RightTrigger,
|
||||
gamepad->LeftStickX,
|
||||
gamepad->LeftStickY,
|
||||
gamepad->RightStickX,
|
||||
gamepad->RightStickY);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::gamepadButtonPressEvent(int deviceId, QGamepadManager::GamepadButton button, double value)
|
||||
{
|
||||
handleGamepadButtonEvent(deviceId, button, value);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::gamepadButtonReleaseEvent(int deviceId, QGamepadManager::GamepadButton button)
|
||||
{
|
||||
handleGamepadButtonEvent(deviceId, button, 0);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::gamepadConnected(int deviceId)
|
||||
{
|
||||
Q_ASSERT(!m_Gamepads.contains(deviceId));
|
||||
|
||||
// Find an unallocated gamepad index and reserve it
|
||||
GamepadState* gamepad = new GamepadState();
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((m_ActiveGamepadMask & (1 << i)) == 0)
|
||||
{
|
||||
m_ActiveGamepadMask |= (1 << i);
|
||||
gamepad->Index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "New gamepad connected " << deviceId << " at index " << gamepad->Index;
|
||||
|
||||
m_Gamepads.insert(deviceId, gamepad);
|
||||
}
|
||||
|
||||
void
|
||||
StreamWidget::gamepadDisconnected(int deviceId)
|
||||
{
|
||||
GamepadState* gamepad = m_Gamepads.take(deviceId);
|
||||
Q_ASSERT(gamepad != nullptr);
|
||||
|
||||
qDebug() << "Gamepad disconnected " << deviceId << " at index " << gamepad->Index;
|
||||
|
||||
// Clear the allocated gamepad index
|
||||
m_ActiveGamepadMask &= ~(1 << gamepad->Index);
|
||||
|
||||
delete gamepad;
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QGamepad>
|
||||
|
||||
class GamepadState
|
||||
{
|
||||
public:
|
||||
GamepadState() :
|
||||
ButtonFlags(0),
|
||||
LeftStickX(0),
|
||||
LeftStickY(0),
|
||||
RightStickX(0),
|
||||
RightStickY(0),
|
||||
LeftTrigger(0),
|
||||
RightTrigger(0) {}
|
||||
|
||||
short Index;
|
||||
short ButtonFlags;
|
||||
short LeftStickX, LeftStickY;
|
||||
short RightStickX, RightStickY;
|
||||
unsigned char LeftTrigger, RightTrigger;
|
||||
};
|
||||
|
||||
class StreamWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit StreamWidget(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void
|
||||
mouseMoveEvent(QMouseEvent *event);
|
||||
|
||||
void
|
||||
wheelEvent(QWheelEvent *event);
|
||||
|
||||
void
|
||||
mousePressEvent(QMouseEvent *event);
|
||||
|
||||
void
|
||||
mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
void
|
||||
keyPressEvent(QKeyEvent *event);
|
||||
|
||||
void
|
||||
keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
void
|
||||
gamepadConnected(int deviceId);
|
||||
|
||||
void
|
||||
gamepadDisconnected(int deviceId);
|
||||
|
||||
void
|
||||
gamepadAxisEvent(int deviceId, QGamepadManager::GamepadAxis axis, double value);
|
||||
|
||||
void
|
||||
gamepadButtonPressEvent(int deviceId, QGamepadManager::GamepadButton button, double value);
|
||||
|
||||
void
|
||||
gamepadButtonReleaseEvent(int deviceId, QGamepadManager::GamepadButton button);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
void
|
||||
handleMouseButtonEvent(QMouseEvent *event);
|
||||
|
||||
void
|
||||
handleKeyEvent(QKeyEvent *event);
|
||||
|
||||
void
|
||||
handleGamepadButtonEvent(int deviceId, QGamepadManager::GamepadButton button, double value);
|
||||
|
||||
int m_LastMouseX, m_LastMouseY;
|
||||
int m_ScrollDeltaX;
|
||||
short m_ActiveGamepadMask;
|
||||
QMap<int, GamepadState*> m_Gamepads;
|
||||
};
|
|
@ -2,3 +2,4 @@
|
|||
|
||||
#define THROW_BAD_ALLOC_IF_NULL(x) \
|
||||
if ((x) == nullptr) throw new std::bad_alloc()
|
||||
|
||||
|
|
Loading…
Reference in a new issue