mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
3d9d162dd6
Add love.dropbegan(), love.dropmoved(x, y), and love.dropcompleted(x, y). Resolves #1346. Resolves #2019.
769 lines
22 KiB
C++
769 lines
22 KiB
C++
/**
|
|
* Copyright (c) 2006-2024 LOVE Development Team
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
**/
|
|
|
|
#include "Event.h"
|
|
|
|
#include "common/int.h"
|
|
#include "filesystem/NativeFile.h"
|
|
#include "filesystem/Filesystem.h"
|
|
#include "keyboard/sdl/Keyboard.h"
|
|
#include "joystick/JoystickModule.h"
|
|
#include "touch/sdl/Touch.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "window/Window.h"
|
|
#include "common/Exception.h"
|
|
#include "audio/Audio.h"
|
|
#include "common/config.h"
|
|
#include "timer/Timer.h"
|
|
#include "sensor/sdl/Sensor.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include "joystick/sdl/Joystick.h"
|
|
|
|
namespace love
|
|
{
|
|
namespace event
|
|
{
|
|
namespace sdl
|
|
{
|
|
|
|
// SDL reports mouse coordinates in the window coordinate system in OS X, but
|
|
// we want them in pixel coordinates (may be different with high-DPI enabled.)
|
|
static void windowToDPICoords(double *x, double *y)
|
|
{
|
|
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
|
if (window)
|
|
window->windowToDPICoords(x, y);
|
|
}
|
|
|
|
static void clampToWindow(double *x, double *y)
|
|
{
|
|
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
|
if (window)
|
|
window->clampPositionInWindow(x, y);
|
|
}
|
|
|
|
static void normalizedToDPICoords(double *x, double *y)
|
|
{
|
|
double w = 1.0, h = 1.0;
|
|
|
|
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
|
if (window)
|
|
{
|
|
w = window->getWidth();
|
|
h = window->getHeight();
|
|
window->windowToDPICoords(&w, &h);
|
|
}
|
|
|
|
if (x)
|
|
*x = ((*x) * w);
|
|
if (y)
|
|
*y = ((*y) * h);
|
|
}
|
|
|
|
// SDL's event watch callbacks trigger when the event is actually posted inside
|
|
// SDL, unlike with SDL_PollEvents. This is useful for some events which require
|
|
// handling inside the function which triggered them on some backends.
|
|
static bool SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event)
|
|
{
|
|
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
|
|
|
switch (event->type)
|
|
{
|
|
// On iOS, calling any OpenGL ES function after the function which triggers
|
|
// SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it
|
|
// with an event watch callback, which will be called inside that function.
|
|
case SDL_EVENT_DID_ENTER_BACKGROUND:
|
|
case SDL_EVENT_WILL_ENTER_FOREGROUND:
|
|
if (gfx)
|
|
gfx->setActive(event->type == SDL_EVENT_WILL_ENTER_FOREGROUND);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Event::Event()
|
|
: love::event::Event("love.event.sdl")
|
|
{
|
|
if (!SDL_InitSubSystem(SDL_INIT_EVENTS))
|
|
throw love::Exception("Could not initialize SDL events subsystem (%s)", SDL_GetError());
|
|
|
|
SDL_AddEventWatch(watchAppEvents, this);
|
|
}
|
|
|
|
Event::~Event()
|
|
{
|
|
SDL_RemoveEventWatch(watchAppEvents, this);
|
|
SDL_QuitSubSystem(SDL_INIT_EVENTS);
|
|
}
|
|
|
|
void Event::pump(float waitTimeout)
|
|
{
|
|
exceptionIfInRenderPass("love.event.pump");
|
|
|
|
int waitTimeoutMS = 0;
|
|
if (std::isinf(waitTimeout) || waitTimeout < 0.0f)
|
|
waitTimeoutMS = -1; // Wait forever.
|
|
else if (waitTimeout > 0.0f)
|
|
waitTimeoutMS = (int)std::min<int64>(LOVE_INT32_MAX, 1000LL * waitTimeout);
|
|
|
|
// Wait for the first event, if requested.
|
|
SDL_Event e;
|
|
if (SDL_WaitEventTimeout(&e, waitTimeoutMS))
|
|
{
|
|
StrongRef<Message> msg(convert(e), Acquire::NORETAIN);
|
|
if (msg)
|
|
push(msg);
|
|
|
|
// Fetch any extra events that came in during WaitEvent.
|
|
while (SDL_PollEvent(&e))
|
|
{
|
|
msg.set(convert(e), Acquire::NORETAIN);
|
|
if (msg)
|
|
push(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
Message *Event::wait()
|
|
{
|
|
exceptionIfInRenderPass("love.event.wait");
|
|
|
|
SDL_Event e;
|
|
|
|
if (!SDL_WaitEvent(&e))
|
|
return nullptr;
|
|
|
|
return convert(e);
|
|
}
|
|
|
|
void Event::clear()
|
|
{
|
|
exceptionIfInRenderPass("love.event.clear");
|
|
|
|
SDL_Event e;
|
|
|
|
while (SDL_PollEvent(&e))
|
|
{
|
|
// Do nothing with 'e' ...
|
|
}
|
|
|
|
love::event::Event::clear();
|
|
}
|
|
|
|
void Event::exceptionIfInRenderPass(const char *name)
|
|
{
|
|
// Some core OS graphics functionality (e.g. swap buffers on some platforms)
|
|
// happens inside SDL_PumpEvents - which is called by SDL_PollEvent and
|
|
// friends. It's probably a bad idea to call those functions while a RT
|
|
// is active.
|
|
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
|
if (gfx != nullptr && gfx->isRenderTargetActive())
|
|
throw love::Exception("%s cannot be called while a render target is active in love.graphics.", name);
|
|
}
|
|
|
|
Message *Event::convert(const SDL_Event &e)
|
|
{
|
|
Message *msg = nullptr;
|
|
|
|
std::vector<Variant> vargs;
|
|
vargs.reserve(4);
|
|
|
|
love::filesystem::Filesystem *filesystem = nullptr;
|
|
love::sensor::Sensor *sensorInstance = nullptr;
|
|
|
|
love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
|
|
love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
|
|
|
|
const char *txt;
|
|
const char *txt2;
|
|
|
|
love::touch::sdl::Touch *touchmodule = nullptr;
|
|
love::touch::Touch::TouchInfo touchinfo = {};
|
|
|
|
switch (e.type)
|
|
{
|
|
case SDL_EVENT_KEY_DOWN:
|
|
if (e.key.repeat)
|
|
{
|
|
auto kb = Module::getInstance<love::keyboard::Keyboard>(Module::M_KEYBOARD);
|
|
if (kb && !kb->hasKeyRepeat())
|
|
break;
|
|
}
|
|
|
|
love::keyboard::sdl::Keyboard::getConstant(e.key.key, key);
|
|
if (!love::keyboard::Keyboard::getConstant(key, txt))
|
|
txt = "unknown";
|
|
|
|
love::keyboard::sdl::Keyboard::getConstant(e.key.scancode, scancode);
|
|
if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
|
|
txt2 = "unknown";
|
|
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
vargs.emplace_back(txt2, strlen(txt2));
|
|
vargs.emplace_back(e.key.repeat != 0);
|
|
msg = new Message("keypressed", vargs);
|
|
break;
|
|
case SDL_EVENT_KEY_UP:
|
|
love::keyboard::sdl::Keyboard::getConstant(e.key.key, key);
|
|
if (!love::keyboard::Keyboard::getConstant(key, txt))
|
|
txt = "unknown";
|
|
|
|
love::keyboard::sdl::Keyboard::getConstant(e.key.scancode, scancode);
|
|
if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
|
|
txt2 = "unknown";
|
|
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
vargs.emplace_back(txt2, strlen(txt2));
|
|
msg = new Message("keyreleased", vargs);
|
|
break;
|
|
case SDL_EVENT_TEXT_INPUT:
|
|
txt = e.text.text;
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
msg = new Message("textinput", vargs);
|
|
break;
|
|
case SDL_EVENT_TEXT_EDITING:
|
|
txt = e.edit.text;
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
vargs.emplace_back((double) e.edit.start);
|
|
vargs.emplace_back((double) e.edit.length);
|
|
msg = new Message("textedited", vargs);
|
|
break;
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
{
|
|
double x = (double) e.motion.x;
|
|
double y = (double) e.motion.y;
|
|
double xrel = (double) e.motion.xrel;
|
|
double yrel = (double) e.motion.yrel;
|
|
|
|
// SDL reports mouse coordinates outside the window bounds when click-and-
|
|
// dragging. For compatibility we clamp instead since user code may not be
|
|
// able to handle out-of-bounds coordinates. SDL has a hint to turn off
|
|
// auto capture, but it doesn't report the mouse's position at the edge of
|
|
// the window if the mouse moves fast enough when it's off.
|
|
clampToWindow(&x, &y);
|
|
windowToDPICoords(&x, &y);
|
|
windowToDPICoords(&xrel, &yrel);
|
|
|
|
vargs.emplace_back(x);
|
|
vargs.emplace_back(y);
|
|
vargs.emplace_back(xrel);
|
|
vargs.emplace_back(yrel);
|
|
vargs.emplace_back(e.motion.which == SDL_TOUCH_MOUSEID);
|
|
msg = new Message("mousemoved", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
|
{
|
|
// SDL uses button 3 for the right mouse button, but we use button 2
|
|
int button = e.button.button;
|
|
switch (button)
|
|
{
|
|
case SDL_BUTTON_RIGHT:
|
|
button = 2;
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
button = 3;
|
|
break;
|
|
}
|
|
|
|
double px = (double) e.button.x;
|
|
double py = (double) e.button.y;
|
|
|
|
clampToWindow(&px, &py);
|
|
windowToDPICoords(&px, &py);
|
|
|
|
vargs.emplace_back(px);
|
|
vargs.emplace_back(py);
|
|
vargs.emplace_back((double) button);
|
|
vargs.emplace_back(e.button.which == SDL_TOUCH_MOUSEID);
|
|
vargs.emplace_back((double) e.button.clicks);
|
|
|
|
bool down = e.type == SDL_EVENT_MOUSE_BUTTON_DOWN;
|
|
msg = new Message(down ? "mousepressed" : "mousereleased", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_MOUSE_WHEEL:
|
|
vargs.emplace_back((double) e.wheel.x);
|
|
vargs.emplace_back((double) e.wheel.y);
|
|
|
|
txt = e.wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? "flipped" : "standard";
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
|
|
msg = new Message("wheelmoved", vargs);
|
|
break;
|
|
case SDL_EVENT_FINGER_DOWN:
|
|
case SDL_EVENT_FINGER_UP:
|
|
case SDL_EVENT_FINGER_MOTION:
|
|
touchinfo.id = (int64)e.tfinger.fingerID;
|
|
touchinfo.x = e.tfinger.x;
|
|
touchinfo.y = e.tfinger.y;
|
|
touchinfo.dx = e.tfinger.dx;
|
|
touchinfo.dy = e.tfinger.dy;
|
|
touchinfo.pressure = e.tfinger.pressure;
|
|
touchinfo.deviceType = love::touch::sdl::Touch::getDeviceType(SDL_GetTouchDeviceType(e.tfinger.touchID));
|
|
touchinfo.mouse = e.tfinger.touchID == SDL_MOUSE_TOUCHID;
|
|
|
|
// SDL's coords are normalized to [0, 1], but we want screen coords for direct touches.
|
|
if (touchinfo.deviceType == love::touch::Touch::DEVICE_TOUCHSCREEN)
|
|
{
|
|
normalizedToDPICoords(&touchinfo.x, &touchinfo.y);
|
|
normalizedToDPICoords(&touchinfo.dx, &touchinfo.dy);
|
|
}
|
|
|
|
// We need to update the love.touch.sdl internal state from here.
|
|
touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
|
|
if (touchmodule)
|
|
touchmodule->onEvent(e.type, touchinfo);
|
|
|
|
if (!love::touch::Touch::getConstant(touchinfo.deviceType, txt))
|
|
txt = "unknown";
|
|
|
|
// This is a bit hackish and we lose the higher 32 bits of the id on
|
|
// 32-bit systems, but SDL only ever gives id's that at most use as many
|
|
// bits as can fit in a pointer (for now.)
|
|
// We use lightuserdata instead of a lua_Number (double) because doubles
|
|
// can't represent all possible id values on 64-bit systems.
|
|
vargs.emplace_back((void *)(intptr_t)touchinfo.id);
|
|
vargs.emplace_back(touchinfo.x);
|
|
vargs.emplace_back(touchinfo.y);
|
|
vargs.emplace_back(touchinfo.dx);
|
|
vargs.emplace_back(touchinfo.dy);
|
|
vargs.emplace_back(touchinfo.pressure);
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
vargs.emplace_back(touchinfo.mouse);
|
|
|
|
if (e.type == SDL_EVENT_FINGER_DOWN)
|
|
txt = "touchpressed";
|
|
else if (e.type == SDL_EVENT_FINGER_UP)
|
|
txt = "touchreleased";
|
|
else
|
|
txt = "touchmoved";
|
|
msg = new Message(txt, vargs);
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
|
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
|
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
|
|
case SDL_EVENT_JOYSTICK_HAT_MOTION:
|
|
case SDL_EVENT_JOYSTICK_ADDED:
|
|
case SDL_EVENT_JOYSTICK_REMOVED:
|
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
|
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
|
msg = convertJoystickEvent(e);
|
|
break;
|
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
|
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
|
case SDL_EVENT_WINDOW_SHOWN:
|
|
case SDL_EVENT_WINDOW_HIDDEN:
|
|
case SDL_EVENT_WINDOW_RESIZED:
|
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
|
case SDL_EVENT_WINDOW_RESTORED:
|
|
case SDL_EVENT_WINDOW_EXPOSED:
|
|
case SDL_EVENT_WINDOW_OCCLUDED:
|
|
msg = convertWindowEvent(e);
|
|
break;
|
|
case SDL_EVENT_DISPLAY_ORIENTATION:
|
|
{
|
|
auto orientation = window::Window::ORIENTATION_UNKNOWN;
|
|
switch ((SDL_DisplayOrientation) e.display.data1)
|
|
{
|
|
case SDL_ORIENTATION_UNKNOWN:
|
|
default:
|
|
orientation = window::Window::ORIENTATION_UNKNOWN;
|
|
break;
|
|
case SDL_ORIENTATION_LANDSCAPE:
|
|
orientation = window::Window::ORIENTATION_LANDSCAPE;
|
|
break;
|
|
case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
|
|
orientation = window::Window::ORIENTATION_LANDSCAPE_FLIPPED;
|
|
break;
|
|
case SDL_ORIENTATION_PORTRAIT:
|
|
orientation = window::Window::ORIENTATION_PORTRAIT;
|
|
break;
|
|
case SDL_ORIENTATION_PORTRAIT_FLIPPED:
|
|
orientation = window::Window::ORIENTATION_PORTRAIT_FLIPPED;
|
|
break;
|
|
}
|
|
|
|
if (!window::Window::getConstant(orientation, txt))
|
|
txt = "unknown";
|
|
|
|
int count = 0;
|
|
int displayindex = 0;
|
|
SDL_DisplayID *displays = SDL_GetDisplays(&count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (displays[i] == e.display.displayID)
|
|
{
|
|
displayindex = i;
|
|
break;
|
|
}
|
|
}
|
|
SDL_free(displays);
|
|
vargs.emplace_back((double)(displayindex + 1));
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
|
|
msg = new Message("displayrotated", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_DROP_BEGIN:
|
|
msg = new Message("dropbegan", vargs);
|
|
break;
|
|
case SDL_EVENT_DROP_COMPLETE:
|
|
{
|
|
double x = e.drop.x;
|
|
double y = e.drop.y;
|
|
windowToDPICoords(&x, &y);
|
|
vargs.emplace_back(x);
|
|
vargs.emplace_back(y);
|
|
msg = new Message("dropcompleted", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_DROP_POSITION:
|
|
{
|
|
double x = e.drop.x;
|
|
double y = e.drop.y;
|
|
windowToDPICoords(&x, &y);
|
|
vargs.emplace_back(x);
|
|
vargs.emplace_back(y);
|
|
msg = new Message("dropmoved", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_DROP_FILE:
|
|
filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
|
|
if (filesystem != nullptr)
|
|
{
|
|
const char *filepath = e.drop.data;
|
|
// Allow mounting any dropped path, so zips or dirs can be mounted.
|
|
filesystem->allowMountingForPath(filepath);
|
|
|
|
double x = e.drop.x;
|
|
double y = e.drop.y;
|
|
windowToDPICoords(&x, &y);
|
|
|
|
if (filesystem->isRealDirectory(filepath))
|
|
{
|
|
vargs.emplace_back(filepath, strlen(filepath));
|
|
vargs.emplace_back(x);
|
|
vargs.emplace_back(y);
|
|
msg = new Message("directorydropped", vargs);
|
|
}
|
|
else
|
|
{
|
|
auto *file = new love::filesystem::NativeFile(filepath, love::filesystem::File::MODE_CLOSED);
|
|
vargs.emplace_back(&love::filesystem::NativeFile::type, file);
|
|
vargs.emplace_back(x);
|
|
vargs.emplace_back(y);
|
|
msg = new Message("filedropped", vargs);
|
|
file->release();
|
|
}
|
|
}
|
|
break;
|
|
case SDL_EVENT_QUIT:
|
|
case SDL_EVENT_TERMINATING:
|
|
msg = new Message("quit");
|
|
break;
|
|
case SDL_EVENT_LOW_MEMORY:
|
|
msg = new Message("lowmemory");
|
|
break;
|
|
case SDL_EVENT_LOCALE_CHANGED:
|
|
msg = new Message("localechanged");
|
|
break;
|
|
case SDL_EVENT_SENSOR_UPDATE:
|
|
sensorInstance = Module::getInstance<sensor::Sensor>(M_SENSOR);
|
|
if (sensorInstance)
|
|
{
|
|
std::vector<void*> sensors = sensorInstance->getHandles();
|
|
|
|
for (void *s: sensors)
|
|
{
|
|
SDL_Sensor *sensor = (SDL_Sensor *) s;
|
|
SDL_SensorID id = SDL_GetSensorID(sensor);
|
|
|
|
if (e.sensor.which == id)
|
|
{
|
|
// Found sensor
|
|
const char *sensorType;
|
|
auto sdltype = SDL_GetSensorType(sensor);
|
|
if (!sensor::Sensor::getConstant(sensor::sdl::Sensor::convert(sdltype), sensorType))
|
|
sensorType = "unknown";
|
|
|
|
vargs.emplace_back(sensorType, strlen(sensorType));
|
|
// Both accelerometer and gyroscope only pass up to 3 values.
|
|
// https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_sensor.h#L81-L127
|
|
vargs.emplace_back(e.sensor.data[0]);
|
|
vargs.emplace_back(e.sensor.data[1]);
|
|
vargs.emplace_back(e.sensor.data[2]);
|
|
msg = new Message("sensorupdated", vargs);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
Message *Event::convertJoystickEvent(const SDL_Event &e) const
|
|
{
|
|
auto joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK);
|
|
if (!joymodule)
|
|
return nullptr;
|
|
|
|
Message *msg = nullptr;
|
|
|
|
std::vector<Variant> vargs;
|
|
vargs.reserve(4);
|
|
|
|
love::Type *joysticktype = &love::joystick::Joystick::type;
|
|
love::joystick::Joystick *stick = nullptr;
|
|
love::joystick::Joystick::Hat hat;
|
|
love::joystick::Joystick::GamepadButton padbutton;
|
|
love::joystick::Joystick::GamepadAxis padaxis;
|
|
const char *txt;
|
|
|
|
switch (e.type)
|
|
{
|
|
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
|
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
|
stick = joymodule->getJoystickFromID(e.jbutton.which);
|
|
if (!stick)
|
|
break;
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back((double)(e.jbutton.button+1));
|
|
msg = new Message((e.type == SDL_EVENT_JOYSTICK_BUTTON_DOWN) ?
|
|
"joystickpressed" : "joystickreleased",
|
|
vargs);
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
|
|
{
|
|
stick = joymodule->getJoystickFromID(e.jaxis.which);
|
|
if (!stick)
|
|
break;
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back((double)(e.jaxis.axis+1));
|
|
float value = joystick::Joystick::clampval(e.jaxis.value / 32768.0f);
|
|
vargs.emplace_back((double) value);
|
|
msg = new Message("joystickaxis", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_HAT_MOTION:
|
|
if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt))
|
|
break;
|
|
|
|
stick = joymodule->getJoystickFromID(e.jhat.which);
|
|
if (!stick)
|
|
break;
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back((double)(e.jhat.hat+1));
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
msg = new Message("joystickhat", vargs);
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
|
{
|
|
const auto &b = e.gbutton;
|
|
if (!joystick::sdl::Joystick::getConstant((SDL_GamepadButton) b.button, padbutton))
|
|
break;
|
|
|
|
if (!joystick::Joystick::getConstant(padbutton, txt))
|
|
break;
|
|
|
|
stick = joymodule->getJoystickFromID(b.which);
|
|
if (!stick)
|
|
break;
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
msg = new Message(e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ?
|
|
"gamepadpressed" : "gamepadreleased", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
|
if (joystick::sdl::Joystick::getConstant((SDL_GamepadAxis) e.gaxis.axis, padaxis))
|
|
{
|
|
if (!joystick::Joystick::getConstant(padaxis, txt))
|
|
break;
|
|
|
|
const auto &a = e.gaxis;
|
|
stick = joymodule->getJoystickFromID(a.which);
|
|
if (!stick)
|
|
break;
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back(txt, strlen(txt));
|
|
float value = joystick::Joystick::clampval(a.value / 32768.0f);
|
|
vargs.emplace_back((double) value);
|
|
msg = new Message("gamepadaxis", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_ADDED:
|
|
// jdevice.which is the joystick device index.
|
|
stick = joymodule->addJoystick(e.jdevice.which);
|
|
if (stick)
|
|
{
|
|
vargs.emplace_back(joysticktype, stick);
|
|
msg = new Message("joystickadded", vargs);
|
|
}
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_REMOVED:
|
|
// jdevice.which is the joystick instance ID now.
|
|
stick = joymodule->getJoystickFromID(e.jdevice.which);
|
|
if (stick)
|
|
{
|
|
joymodule->removeJoystick(stick);
|
|
vargs.emplace_back(joysticktype, stick);
|
|
msg = new Message("joystickremoved", vargs);
|
|
}
|
|
break;
|
|
#if defined(LOVE_ENABLE_SENSOR)
|
|
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
|
{
|
|
const auto &sens = e.gsensor;
|
|
stick = joymodule->getJoystickFromID(sens.which);
|
|
if (stick)
|
|
{
|
|
using Sensor = love::sensor::Sensor;
|
|
|
|
const char *sensorName;
|
|
Sensor::SensorType sensorType = love::sensor::sdl::Sensor::convert((SDL_SensorType) sens.sensor);
|
|
if (!Sensor::getConstant(sensorType, sensorName))
|
|
sensorName = "unknown";
|
|
|
|
vargs.emplace_back(joysticktype, stick);
|
|
vargs.emplace_back(sensorName, strlen(sensorName));
|
|
vargs.emplace_back(sens.data[0]);
|
|
vargs.emplace_back(sens.data[1]);
|
|
vargs.emplace_back(sens.data[2]);
|
|
msg = new Message("joysticksensorupdated", vargs);
|
|
}
|
|
}
|
|
break;
|
|
#endif // defined(LOVE_ENABLE_SENSOR)
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
Message *Event::convertWindowEvent(const SDL_Event &e)
|
|
{
|
|
Message *msg = nullptr;
|
|
|
|
std::vector<Variant> vargs;
|
|
vargs.reserve(4);
|
|
|
|
window::Window *win = nullptr;
|
|
graphics::Graphics *gfx = nullptr;
|
|
|
|
auto event = e.type;
|
|
|
|
switch (event)
|
|
{
|
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
vargs.emplace_back(event == SDL_EVENT_WINDOW_FOCUS_GAINED);
|
|
msg = new Message("focus", vargs);
|
|
break;
|
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
|
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
|
vargs.emplace_back(event == SDL_EVENT_WINDOW_MOUSE_ENTER);
|
|
msg = new Message("mousefocus", vargs);
|
|
break;
|
|
case SDL_EVENT_WINDOW_SHOWN:
|
|
case SDL_EVENT_WINDOW_HIDDEN:
|
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
|
case SDL_EVENT_WINDOW_RESTORED:
|
|
#ifdef LOVE_ANDROID
|
|
if (auto audio = Module::getInstance<audio::Audio>(Module::M_AUDIO))
|
|
{
|
|
if (event == SDL_EVENT_WINDOW_MINIMIZED)
|
|
audio->pauseContext();
|
|
else if (event == SDL_EVENT_WINDOW_RESTORED)
|
|
audio->resumeContext();
|
|
}
|
|
#endif
|
|
// WINDOW_RESTORED can also happen when going from maximized -> unmaximized,
|
|
// but there isn't a nice way to avoid sending our event in that situation.
|
|
vargs.emplace_back(event == SDL_EVENT_WINDOW_SHOWN || event == SDL_EVENT_WINDOW_RESTORED);
|
|
msg = new Message("visible", vargs);
|
|
break;
|
|
case SDL_EVENT_WINDOW_EXPOSED:
|
|
msg = new Message("exposed");
|
|
break;
|
|
case SDL_EVENT_WINDOW_OCCLUDED:
|
|
msg = new Message("occluded");
|
|
break;
|
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
|
{
|
|
double width = e.window.data1;
|
|
double height = e.window.data2;
|
|
|
|
gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
|
win = Module::getInstance<window::Window>(Module::M_WINDOW);
|
|
if (win)
|
|
win->onSizeChanged(e.window.data1, e.window.data2);
|
|
|
|
// The size values in the Window aren't necessarily the same as the
|
|
// graphics size, which is what we want to output.
|
|
if (gfx)
|
|
{
|
|
width = gfx->getWidth();
|
|
height = gfx->getHeight();
|
|
}
|
|
else if (win)
|
|
{
|
|
width = win->getWidth();
|
|
height = win->getHeight();
|
|
windowToDPICoords(&width, &height);
|
|
}
|
|
|
|
vargs.emplace_back(width);
|
|
vargs.emplace_back(height);
|
|
msg = new Message("resize", vargs);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
} // sdl
|
|
} // event
|
|
} // love
|