Reorganize and prepare for SDL integration

This commit is contained in:
Cameron Gutman committed 2018-06-23 18:46:59 -07:00
1 parent ea459a0319
commit ce64966843
18 files changed
+637 -586

No files matched your search

+14 -14
View File
@@ -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.
View File
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
View File
@@ -1,4 +1,4 @@
#include "mainwindow.h"
#include "gui/mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
+130
View 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
View 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;
}
}
-478
View File
@@ -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;
}
-86
View File
@@ -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;
};
+1
View File
@@ -2,3 +2,4 @@
#define THROW_BAD_ALLOC_IF_NULL(x) \
if ((x) == nullptr) throw new std::bad_alloc()