mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-10-21 15:57:12 +00:00
Reorganize and prepare for SDL integration
This commit is contained in:
1 parent
ea459a0319
commit
ce64966843
18 files changed
+637
-586
No files matched your search
+14
-14
@@ -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),
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
#include "mainwindow.h"
|
||||
#include "gui/mainwindow.h"
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user