Created
July 19, 2011 22:00
-
-
Save beoran/1093843 to your computer and use it in GitHub Desktop.
Milkytracker CANOO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tracker/sdl/SDL_Main.cpp | |
* | |
* Copyright 2009 Peter Barth, Christopher O'Neill | |
* | |
* This file is part of Milkytracker. | |
* | |
* Milkytracker is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* Milkytracker is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with Milkytracker. If not, see <http://www.gnu.org/licenses/>. | |
* | |
*/ | |
/* | |
* SDL_Main.cpp | |
* MilkyTracker SDL front end | |
* | |
* Created by Peter Barth on 19.11.05. | |
* | |
* 19/7/11 - beoran@rubyforge.org & com64 | |
* Modifications for caanoo. Sorry, Peter, to make everything more messy. | |
* | |
* 15/2/08 - Peter Barth | |
* This code needs major clean up, there are too many workarounds going on | |
* for different platforms/configurations (MIDI, GP2X etc.) | |
* Please do not further pollute this single source code when possible | |
* | |
* 14/8/06 - Christopher O'Neill | |
* Ok, there are so many changes in this file that I've lost track... | |
* Here are some I remember: | |
* - ALSA Midi Support | |
* - GP2X mouse emulator (awaiting a rewrite one day..) | |
* - Various command line options | |
* - Fix for french azerty keyboards (their number keys are shifted) | |
* | |
*/ | |
#ifdef HAVE_CONFIG_H | |
#include "config.h" | |
#endif | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#include <signal.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <SDL.h> | |
#ifndef __QTOPIA__ | |
#ifdef HAVE_X11 | |
#include <SDL_syswm.h> | |
#endif | |
#endif | |
#include "SDL_KeyTranslation.h" | |
// ---------------------------- Tracker includes ---------------------------- | |
#include "PPUI.h" | |
#include "DisplayDevice_SDL.h" | |
#include "DisplayDeviceFB_SDL.h" | |
#ifdef __OPENGL__ | |
#include "DisplayDeviceOGL_SDL.h" // <-- Experimental, slow | |
#endif | |
#include "Screen.h" | |
#include "Tracker.h" | |
#include "PPMutex.h" | |
#include "PPSystem_POSIX.h" | |
#include "PPPath_POSIX.h" | |
#define __CAANOO__ | |
#ifdef HAVE_LIBASOUND | |
#include "../midi/posix/MidiReceiver_pthread.h" | |
#endif | |
// -------------------------------------------------------------------------- | |
// SDL surface screen | |
SDL_Surface* screen = NULL; | |
SDL_TimerID timer; | |
// Tracker globals | |
static PPScreen* myTrackerScreen = NULL; | |
static Tracker* myTracker = NULL; | |
static PPDisplayDevice* myDisplayDevice = NULL; | |
#ifdef HAVE_LIBASOUND | |
static MidiReceiver* myMidiReceiver = NULL; | |
#endif | |
// Okay what else do we need? | |
PPMutex* globalMutex = NULL; | |
static PPMutex* timerMutex = NULL; | |
static bool ticking = false; | |
static pp_uint32 lmyTime; | |
static PPPoint llastClickPosition = PPPoint(0,0); | |
static pp_uint16 lClickCount = 0; | |
static pp_uint32 rmyTime; | |
static PPPoint rlastClickPosition = PPPoint(0,0); | |
static pp_uint16 rClickCount = 0; | |
static bool lMouseDown = false; | |
static pp_uint32 lButtonDownStartTime; | |
static bool rMouseDown = false; | |
static pp_uint32 rButtonDownStartTime; | |
static pp_uint32 timerTicker = 0; | |
static PPPoint p; | |
// This needs to be visible from outside | |
pp_uint32 PPGetTickCount() | |
{ | |
return SDL_GetTicks(); | |
} | |
// Same as above | |
void QueryKeyModifiers() | |
{ | |
pp_uint32 mod = SDL_GetModState(); | |
if((mod & KMOD_LSHIFT) || (mod & KMOD_RSHIFT)) | |
setKeyModifier(KeyModifierSHIFT); | |
else | |
clearKeyModifier(KeyModifierSHIFT); | |
if((mod & KMOD_LCTRL) || (mod & KMOD_RCTRL)) | |
setKeyModifier(KeyModifierCTRL); | |
else | |
clearKeyModifier(KeyModifierCTRL); | |
if((mod & KMOD_LALT) || (mod & KMOD_RALT)) | |
setKeyModifier(KeyModifierALT); | |
else | |
clearKeyModifier(KeyModifierALT); | |
} | |
static void RaiseEventSerialized(PPEvent* event) | |
{ | |
if (myTrackerScreen && myTracker) | |
{ | |
globalMutex->lock(); | |
myTrackerScreen->raiseEvent(event); | |
globalMutex->unlock(); | |
} | |
} | |
#if defined(__GP2X__) || defined(__CAANOO__) | |
struct MouseEmulation | |
{ | |
bool up, down, left, right, upLeft, upRight, downLeft, downRight; | |
int x, y; | |
int ticks; | |
int button; | |
} ; | |
MouseEmulation mouse; | |
#endif | |
#ifdef __GP2X__ | |
void gp2xMouseEvent(const SDL_Event& event) | |
{ | |
bool buttonPressed; | |
if(event.type == SDL_JOYBUTTONDOWN) | |
buttonPressed = true; | |
else | |
buttonPressed = false; | |
switch(event.jbutton.button) | |
{ | |
case 0: // Up | |
mouse.up = buttonPressed; | |
break; | |
case 4: // Down | |
mouse.down = buttonPressed; | |
break; | |
case 2: // Left | |
mouse.left = buttonPressed; | |
break; | |
case 6: // Right | |
mouse.right = buttonPressed; | |
break; | |
case 1: // upLeft | |
mouse.upLeft = buttonPressed; | |
break; | |
case 7: // upRight | |
mouse.upRight = buttonPressed; | |
break; | |
case 3: // downLeft | |
mouse.downLeft = buttonPressed; | |
break; | |
case 5: // downRight | |
mouse.downRight = buttonPressed; | |
break; | |
case 18: // Click | |
case 12: | |
SDL_Event myEvent; | |
if (buttonPressed) | |
{ | |
myEvent.type = SDL_MOUSEBUTTONDOWN; | |
myEvent.button.type = SDL_MOUSEBUTTONDOWN; | |
myEvent.button.state = SDL_PRESSED; | |
mouse.button = 1; | |
} | |
else | |
{ | |
myEvent.type = SDL_MOUSEBUTTONUP; | |
myEvent.button.type = SDL_MOUSEBUTTONUP; | |
myEvent.button.state = SDL_RELEASED; | |
mouse.button = 0; | |
} | |
myEvent.button.x = mouse.x; | |
myEvent.button.y = mouse.y; | |
myEvent.button.button = SDL_BUTTON_LEFT; | |
SDL_PushEvent(&myEvent); | |
break; | |
case 8: // Start | |
if (!buttonPressed) | |
{ | |
pp_uint16 chr[3] = {VK_RETURN, 0, 0}; | |
PPEvent event(eKeyDown, &chr, sizeof(chr)); | |
} | |
break; | |
} | |
} | |
void gp2xMouseMove() | |
{ | |
const int xMax = 320, yMax = 240; | |
static int oldX = 0, oldY = 0; | |
int step; | |
if(mouse.ticks < 25) | |
step = 1; | |
else if(mouse.ticks < 75) | |
step = 2; | |
else if(mouse.ticks < 125) | |
step = 3; | |
if(mouse.up || mouse.upLeft || mouse.upRight) mouse.y -= step; | |
if(mouse.down || mouse.downLeft || mouse.downRight) mouse.y += step; | |
if(mouse.left || mouse.downLeft || mouse.upLeft) mouse.x -= step; | |
if(mouse.right || mouse.downRight || mouse.upRight) mouse.x += step; | |
if(mouse.x == oldX && mouse.y == oldY) { | |
mouse.ticks = 0; | |
return; | |
} | |
oldX = mouse.x; oldY = mouse.y; | |
if(mouse.x > xMax) mouse.x = xMax; | |
if(mouse.x < 0) mouse.x = 0; | |
if(mouse.y > yMax) mouse.y = yMax; | |
if(mouse.y < 0) mouse.y = 0; | |
SDL_WarpMouse(mouse.x, mouse.y); | |
if(mouse.ticks < 125) mouse.ticks++; | |
} | |
#endif | |
#ifdef __CAANOO__ | |
enum CaanooButtons { | |
CAANOO_BTN_A = 0, | |
CAANOO_BTN_X = 1, | |
CAANOO_BTN_B = 2, | |
CAANOO_BTN_Y = 3, | |
CAANOO_BTN_L = 4, | |
CAANOO_BTN_R = 5, | |
CAANOO_BTN_HOME = 6, // Home / 7, R1 | |
CAANOO_BTN_HOLD = 7, // Hold / 8, R2 | |
CAANOO_BTN_HELP1 = 8, // Help I / Select | |
CAANOO_BTN_HELP2 = 9, // Help II / Start | |
CAANOO_BTN_TACT = 10, // Tact / L Thumb Stick | |
}; | |
#define CAANOO_JSDEADZONE 10 /** "dead zone" of analog joystick. */ | |
void caanooMouseEvent(const SDL_Event& event) | |
{ | |
bool buttonPressed; | |
if(event.type == SDL_JOYBUTTONDOWN) { | |
buttonPressed = true; | |
} else if (event.type == SDL_JOYBUTTONUP) { | |
buttonPressed = false; | |
} | |
if(event.type == SDL_JOYAXISMOTION) { | |
if (event.jaxis.axis == 0) { /* X axis */ | |
mouse.right = false; mouse.left = false; /* Reset x motion. */ | |
if (event.jaxis.value < -CAANOO_JSDEADZONE) { /* left? */ | |
mouse.left = true; | |
} else if (event.jaxis.value > CAANOO_JSDEADZONE) { /* right? */ | |
mouse.right = true; | |
} | |
} else if (event.jaxis.axis == 1) { /* Y axis */ | |
mouse.up = false; mouse.down = false; /* Reset Y motion. */ | |
if (event.jaxis.value < -CAANOO_JSDEADZONE) { /* up? */ | |
mouse.up = true; | |
} else if (event.jaxis.value > CAANOO_JSDEADZONE) { /* down? */ | |
mouse.down = true; | |
} | |
} | |
return; | |
} | |
switch(event.jbutton.button) | |
{ | |
case CAANOO_BTN_A: // Click left | |
case CAANOO_BTN_B: // Click right | |
SDL_Event myEvent; | |
if (buttonPressed) | |
{ | |
myEvent.type = SDL_MOUSEBUTTONDOWN; | |
myEvent.button.type = SDL_MOUSEBUTTONDOWN; | |
myEvent.button.state = SDL_PRESSED; | |
mouse.button = event.jbutton.button + 1; | |
} | |
else | |
{ | |
myEvent.type = SDL_MOUSEBUTTONUP; | |
myEvent.button.type = SDL_MOUSEBUTTONUP; | |
myEvent.button.state = SDL_RELEASED; | |
mouse.button = event.jbutton.button + 1; | |
} | |
myEvent.button.x = mouse.x; | |
myEvent.button.y = mouse.y; | |
myEvent.button.button = SDL_BUTTON_LEFT; | |
SDL_PushEvent(&myEvent); | |
break; | |
case CAANOO_BTN_HOME: // Start | |
if (!buttonPressed) | |
{ | |
pp_uint16 chr[3] = {VK_RETURN, 0, 0}; | |
PPEvent event(eKeyDown, &chr, sizeof(chr)); | |
RaiseEventSerialized(&event); | |
} | |
break; | |
} | |
} | |
void caanooMouseMove() | |
{ | |
const int xMax = 320, yMax = 240; | |
static int oldX = 0, oldY = 0; | |
int step; | |
if(mouse.ticks < 25) | |
step = 1; | |
else if(mouse.ticks < 75) | |
step = 2; | |
else if(mouse.ticks < 125) | |
step = 3; | |
if(mouse.up || mouse.upLeft || mouse.upRight) mouse.y -= step; | |
if(mouse.down || mouse.downLeft || mouse.downRight) mouse.y += step; | |
if(mouse.left || mouse.downLeft || mouse.upLeft) mouse.x -= step; | |
if(mouse.right || mouse.downRight || mouse.upRight) mouse.x += step; | |
if(mouse.x == oldX && mouse.y == oldY) { | |
mouse.ticks = 0; | |
return; | |
} | |
oldX = mouse.x; oldY = mouse.y; | |
if(mouse.x > xMax) mouse.x = xMax; | |
if(mouse.x < 0) mouse.x = 0; | |
if(mouse.y > yMax) mouse.y = yMax; | |
if(mouse.y < 0) mouse.y = 0; | |
SDL_WarpMouse(mouse.x, mouse.y); | |
if(mouse.ticks < 125) mouse.ticks++; | |
} | |
#endif | |
enum SDLUserEvents | |
{ | |
SDLUserEventTimer, | |
SDLUserEventLMouseRepeat, | |
SDLUserEventRMouseRepeat, | |
SDLUserEventMidiKeyDown, | |
SDLUserEventMidiKeyUp, | |
}; | |
static SDLCALL Uint32 timerCallback(Uint32 interval) | |
{ | |
timerMutex->lock(); | |
if (!myTrackerScreen || !myTracker || !ticking) | |
{ | |
timerMutex->unlock(); | |
return interval; | |
} | |
SDL_UserEvent ev; | |
ev.type = SDL_USEREVENT; | |
if (!(timerTicker % 1)) | |
{ | |
ev.code = SDLUserEventTimer; | |
SDL_PushEvent((SDL_Event*)&ev); | |
//PPEvent myEvent(eTimer); | |
//RaiseEventSerialized(&myEvent); | |
} | |
timerTicker++; | |
#if defined(__GP2X__) | |
gp2xMouseMove(); | |
#elif defined(__CAANOO__) | |
caanooMouseMove(); | |
#endif | |
if (lMouseDown && | |
(timerTicker - lButtonDownStartTime) > 25) | |
{ | |
ev.code = SDLUserEventLMouseRepeat; | |
ev.data1 = (void*)p.x; | |
ev.data2 = (void*)p.y; | |
SDL_PushEvent((SDL_Event*)&ev); | |
//PPEvent myEvent(eLMouseRepeat, &p, sizeof(PPPoint)); | |
//RaiseEventSerialized(&myEvent); | |
} | |
if (rMouseDown && | |
(timerTicker - rButtonDownStartTime) > 25) | |
{ | |
ev.code = SDLUserEventRMouseRepeat; | |
ev.data1 = (void*)p.x; | |
ev.data2 = (void*)p.y; | |
SDL_PushEvent((SDL_Event*)&ev); | |
//PPEvent myEvent(eRMouseRepeat, &p, sizeof(PPPoint)); | |
//RaiseEventSerialized(&myEvent); | |
} | |
timerMutex->unlock(); | |
return interval; | |
} | |
#ifdef HAVE_LIBASOUND | |
class MidiEventHandler : public MidiReceiver::MidiEventHandler | |
{ | |
public: | |
virtual void keyDown(int note, int volume) | |
{ | |
SDL_UserEvent ev; | |
ev.type = SDL_USEREVENT; | |
ev.code = SDLUserEventMidiKeyDown; | |
ev.data1 = (void*)note; | |
ev.data2 = (void*)volume; | |
SDL_PushEvent((SDL_Event*)&ev); | |
//globalMutex->lock(); | |
//myTracker->sendNoteDown(note, volume); | |
//globalMutex->unlock(); | |
} | |
virtual void keyUp(int note) | |
{ | |
SDL_UserEvent ev; | |
ev.type = SDL_USEREVENT; | |
ev.code = SDLUserEventMidiKeyUp; | |
ev.data1 = (void*)note; | |
SDL_PushEvent((SDL_Event*)&ev); | |
//globalMutex->lock(); | |
//myTracker->sendNoteUp(note); | |
//globalMutex->unlock(); | |
} | |
} midiEventHandler; | |
void StopMidiRecording() | |
{ | |
if (myMidiReceiver) | |
{ | |
myMidiReceiver->stopRecording(); | |
} | |
} | |
void StartMidiRecording(unsigned int devID) | |
{ | |
if (devID == (unsigned)-1) | |
return; | |
StopMidiRecording(); | |
myMidiReceiver = new MidiReceiver(midiEventHandler); | |
if (!myMidiReceiver->startRecording(devID)) | |
{ | |
// Deal with error | |
fprintf(stderr, "Failed to initialise ALSA MIDI support.\n"); | |
} | |
} | |
void InitMidi() | |
{ | |
StartMidiRecording(0); | |
} | |
#endif | |
void translateMouseDownEvent(pp_int32 mouseButton, pp_int32 localMouseX, pp_int32 localMouseY) | |
{ | |
if (mouseButton > 2 || !mouseButton) | |
return; | |
// ----------------------------- | |
myDisplayDevice->transform(localMouseX, localMouseY); | |
p.x = localMouseX; | |
p.y = localMouseY; | |
if (mouseButton == 1) | |
{ | |
PPEvent myEvent(eLMouseDown, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
lMouseDown = true; | |
lButtonDownStartTime = timerTicker; | |
if (!lClickCount) | |
{ | |
lmyTime = PPGetTickCount(); | |
llastClickPosition.x = localMouseX; | |
llastClickPosition.y = localMouseY; | |
} | |
else if (lClickCount == 2) | |
{ | |
pp_uint32 deltat = PPGetTickCount() - lmyTime; | |
if (deltat > 500) | |
{ | |
lClickCount = 0; | |
lmyTime = PPGetTickCount(); | |
llastClickPosition.x = localMouseX; | |
llastClickPosition.y = localMouseY; | |
} | |
} | |
lClickCount++; | |
} | |
else if (mouseButton == 2) | |
{ | |
PPEvent myEvent(eRMouseDown, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
rMouseDown = true; | |
rButtonDownStartTime = timerTicker; | |
if (!rClickCount) | |
{ | |
rmyTime = PPGetTickCount(); | |
rlastClickPosition.x = localMouseX; | |
rlastClickPosition.y = localMouseY; | |
} | |
else if (rClickCount == 2) | |
{ | |
pp_uint32 deltat = PPGetTickCount() - rmyTime; | |
if (deltat > 500) | |
{ | |
rClickCount = 0; | |
rmyTime = PPGetTickCount(); | |
rlastClickPosition.x = localMouseX; | |
rlastClickPosition.y = localMouseY; | |
} | |
} | |
rClickCount++; | |
} | |
} | |
void translateMouseUpEvent(pp_int32 mouseButton, pp_int32 localMouseX, pp_int32 localMouseY) | |
{ | |
myDisplayDevice->transform(localMouseX, localMouseY); | |
if (mouseButton == SDL_BUTTON_WHEELDOWN) | |
{ | |
TMouseWheelEventParams mouseWheelParams; | |
mouseWheelParams.pos.x = localMouseX; | |
mouseWheelParams.pos.y = localMouseY; | |
mouseWheelParams.delta = -1; | |
PPEvent myEvent(eMouseWheelMoved, &mouseWheelParams, sizeof(mouseWheelParams)); | |
RaiseEventSerialized(&myEvent); | |
} | |
else if (mouseButton == SDL_BUTTON_WHEELUP) | |
{ | |
TMouseWheelEventParams mouseWheelParams; | |
mouseWheelParams.pos.x = localMouseX; | |
mouseWheelParams.pos.y = localMouseY; | |
mouseWheelParams.delta = 1; | |
PPEvent myEvent(eMouseWheelMoved, &mouseWheelParams, sizeof(mouseWheelParams)); | |
RaiseEventSerialized(&myEvent); | |
} | |
else if (mouseButton > 2 || !mouseButton) | |
return; | |
// ----------------------------- | |
if (mouseButton == 1) | |
{ | |
lClickCount++; | |
if (lClickCount >= 4) | |
{ | |
pp_uint32 deltat = PPGetTickCount() - lmyTime; | |
if (deltat < 500) | |
{ | |
p.x = localMouseX; p.y = localMouseY; | |
if (abs(p.x - llastClickPosition.x) < 4 && | |
abs(p.y - llastClickPosition.y) < 4) | |
{ | |
PPEvent myEvent(eLMouseDoubleClick, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
} | |
} | |
lClickCount = 0; | |
} | |
p.x = localMouseX; p.y = localMouseY; | |
PPEvent myEvent(eLMouseUp, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
lMouseDown = false; | |
} | |
else if (mouseButton == 2) | |
{ | |
rClickCount++; | |
if (rClickCount >= 4) | |
{ | |
pp_uint32 deltat = PPGetTickCount() - rmyTime; | |
if (deltat < 500) | |
{ | |
p.x = localMouseX; p.y = localMouseY; | |
if (abs(p.x - rlastClickPosition.x) < 4 && | |
abs(p.y - rlastClickPosition.y) < 4) | |
{ | |
PPEvent myEvent(eRMouseDoubleClick, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
} | |
} | |
rClickCount = 0; | |
} | |
p.x = localMouseX; p.y = localMouseY; | |
PPEvent myEvent(eRMouseUp, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
rMouseDown = false; | |
} | |
} | |
void translateMouseMoveEvent(pp_int32 mouseButton, pp_int32 localMouseX, pp_int32 localMouseY) | |
{ | |
myDisplayDevice->transform(localMouseX, localMouseY); | |
if (mouseButton == 0) | |
{ | |
p.x = localMouseX; p.y = localMouseY; | |
PPEvent myEvent(eMouseMoved, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
} | |
else | |
{ | |
if (mouseButton > 2 || !mouseButton) | |
return; | |
p.x = localMouseX; p.y = localMouseY; | |
if (mouseButton == 1 && lMouseDown) | |
{ | |
PPEvent myEvent(eLMouseDrag, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
} | |
else if (rMouseDown) | |
{ | |
PPEvent myEvent(eRMouseDrag, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
} | |
} | |
} | |
void preTranslateKey(SDL_keysym& keysym) | |
{ | |
// Rotate cursor keys if necessary | |
switch (myDisplayDevice->getOrientation()) | |
{ | |
case PPDisplayDevice::ORIENTATION_ROTATE90CW: | |
switch (keysym.sym) | |
{ | |
case SDLK_UP: | |
keysym.sym = SDLK_LEFT; | |
break; | |
case SDLK_DOWN: | |
keysym.sym = SDLK_RIGHT; | |
break; | |
case SDLK_LEFT: | |
keysym.sym = SDLK_DOWN; | |
break; | |
case SDLK_RIGHT: | |
keysym.sym = SDLK_UP; | |
break; | |
} | |
break; | |
case PPDisplayDevice::ORIENTATION_ROTATE90CCW: | |
switch (keysym.sym) | |
{ | |
case SDLK_DOWN: | |
keysym.sym = SDLK_LEFT; | |
break; | |
case SDLK_UP: | |
keysym.sym = SDLK_RIGHT; | |
break; | |
case SDLK_RIGHT: | |
keysym.sym = SDLK_DOWN; | |
break; | |
case SDLK_LEFT: | |
keysym.sym = SDLK_UP; | |
break; | |
} | |
break; | |
} | |
} | |
void translateKeyDownEvent(const SDL_Event& event) | |
{ | |
SDL_keysym keysym = event.key.keysym; | |
// ALT+RETURN = Fullscreen toggle | |
if (keysym.sym == SDLK_RETURN && (keysym.mod & KMOD_LALT)) | |
{ | |
PPEvent myEvent(eFullScreen); | |
RaiseEventSerialized(&myEvent); | |
return; | |
} | |
preTranslateKey(keysym); | |
pp_uint16 character = event.key.keysym.unicode; | |
pp_uint16 chr[3] = {toVK(keysym), toSC(keysym), character}; | |
#ifndef NOT_PC_KB | |
// Hack for azerty keyboards (num keys are shifted, so we use the scancodes) | |
if (stdKb) | |
{ | |
if (chr[1] >= 2 && chr[1] <= 10) | |
chr[0] = chr[1] + 47; // 1-9 | |
else if (chr[1] == 11) | |
chr[0] = 48; // 0 | |
} | |
#endif | |
PPEvent myEvent(eKeyDown, &chr, sizeof(chr)); | |
RaiseEventSerialized(&myEvent); | |
if(character == 127) character = VK_BACK; | |
if (character >= 32 && character <= 127) | |
{ | |
PPEvent myEvent2(eKeyChar, &character, sizeof(character)); | |
RaiseEventSerialized(&myEvent2); | |
} | |
} | |
void translateKeyUpEvent(const SDL_Event& event) | |
{ | |
SDL_keysym keysym = event.key.keysym; | |
preTranslateKey(keysym); | |
pp_uint16 character = event.key.keysym.unicode; | |
pp_uint16 chr[3] = {toVK(keysym), toSC(keysym), character}; | |
#ifndef NOT_PC_KB | |
if (stdKb) | |
{ | |
if(chr[1] >= 2 && chr[1] <= 10) | |
chr[0] = chr[1] + 47; | |
else if(chr[1] == 11) | |
chr[0] = 48; | |
} | |
#endif | |
PPEvent myEvent(eKeyUp, &chr, sizeof(chr)); | |
RaiseEventSerialized(&myEvent); | |
} | |
void processSDLEvents(const SDL_Event& event) | |
{ | |
pp_uint32 mouseButton = 0; | |
switch (event.type) | |
{ | |
case SDL_MOUSEBUTTONDOWN: | |
mouseButton = event.button.button; | |
if (mouseButton > 1 && mouseButton <= 3) | |
mouseButton = 2; | |
translateMouseDownEvent(mouseButton, event.button.x, event.button.y); | |
break; | |
case SDL_MOUSEBUTTONUP: | |
mouseButton = event.button.button; | |
if (mouseButton > 1 && mouseButton <= 3) | |
mouseButton = 2; | |
translateMouseUpEvent(mouseButton, event.button.x, event.button.y); | |
break; | |
case SDL_MOUSEMOTION: | |
#if defined(__GP2X__) || defined(__CAANOO__) | |
translateMouseMoveEvent(mouse.button, event.motion.x, event.motion.y); | |
#else | |
translateMouseMoveEvent(event.button.button, event.motion.x, event.motion.y); | |
#endif | |
break; | |
case SDL_KEYDOWN: | |
translateKeyDownEvent(event); | |
break; | |
case SDL_KEYUP: | |
translateKeyUpEvent(event); | |
break; | |
#if defined(__GP2X__) | |
case SDL_JOYBUTTONDOWN: | |
case SDL_JOYBUTTONUP: | |
gp2xMouseEvent(event); | |
break; | |
#elif defined(__CAANOO__) | |
case SDL_JOYAXISMOTION: | |
case SDL_JOYBUTTONDOWN: | |
case SDL_JOYBUTTONUP: | |
caanooMouseEvent(event); | |
break; | |
#endif | |
} | |
} | |
void processSDLUserEvents(const SDL_UserEvent& event) | |
{ | |
switch (event.code) | |
{ | |
case SDLUserEventTimer: | |
{ | |
PPEvent myEvent(eTimer); | |
RaiseEventSerialized(&myEvent); | |
break; | |
} | |
case SDLUserEventLMouseRepeat: | |
{ | |
PPPoint p; | |
p.x = (pp_int32)event.data1; | |
p.y = (pp_int32)event.data2; | |
PPEvent myEvent(eLMouseRepeat, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
break; | |
} | |
case SDLUserEventRMouseRepeat: | |
{ | |
PPPoint p; | |
p.x = (pp_int32)event.data1; | |
p.y = (pp_int32)event.data2; | |
PPEvent myEvent(eRMouseRepeat, &p, sizeof(PPPoint)); | |
RaiseEventSerialized(&myEvent); | |
break; | |
} | |
case SDLUserEventMidiKeyDown: | |
{ | |
pp_int32 note = (pp_int32)event.data1; | |
pp_int32 volume = (pp_int32)event.data2; | |
globalMutex->lock(); | |
myTracker->sendNoteDown(note, volume); | |
globalMutex->unlock(); | |
break; | |
} | |
case SDLUserEventMidiKeyUp: | |
{ | |
pp_int32 note = (pp_int32)event.data1; | |
globalMutex->lock(); | |
myTracker->sendNoteUp(note); | |
globalMutex->unlock(); | |
break; | |
} | |
} | |
} | |
#ifdef __unix__ | |
void crashHandler(int signum) | |
{ | |
// Save backup.xm | |
static char buffer[1024]; // Should be enough :p | |
strncpy(buffer, getenv("HOME"), 1010); | |
strcat(buffer, "/BACKUP00.XM"); | |
struct stat statBuf; | |
int num = 1; | |
while(stat(buffer, &statBuf) == 0 && num <= 100) | |
snprintf(buffer, sizeof(buffer), "%s/BACKUP%02i.XM", getenv("HOME"), num++); | |
if (signum == 15) | |
{ | |
fprintf(stderr, "\nTERM signal received.\n"); | |
SDL_Quit(); | |
return; | |
} | |
else | |
{ | |
fprintf(stderr, "\nCrashed with signal %i\n" | |
"Please submit a bug report stating exactly what you were doing " | |
"at the time of the crash, as well as the above signal number. " | |
"Also note if it is possible to reproduce this crash.\n", signum); | |
} | |
if (num != 100) | |
{ | |
myTracker->saveModule(buffer); | |
fprintf(stderr, "\nA backup has been saved to %s\n\n", buffer); | |
} | |
// Try and quit SDL | |
SDL_Quit(); | |
} | |
#endif | |
void initTracker(pp_uint32 bpp, PPDisplayDevice::Orientations orientation, | |
bool swapRedBlue, bool fullScreen, bool noSplash) | |
{ | |
/* Initialize SDL */ | |
if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0 ) | |
{ | |
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); | |
exit(1); | |
} | |
// atexit(SDL_Quit); Not really needed, and needs a wrapper for OS/2 | |
#if defined(__GP2X__) || defined(__CAANOO__) | |
if ( SDL_Init(SDL_INIT_JOYSTICK) < 0 || !SDL_JoystickOpen(0)) | |
{ | |
fprintf(stderr, "Couldn't initialize SDL Joystick: %s\n",SDL_GetError()); | |
exit(1); | |
} | |
mouse.x = 0; mouse.y = 0; | |
mouse.ticks = 0; | |
mouse.button = 0; | |
#endif | |
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, | |
SDL_DEFAULT_REPEAT_INTERVAL); | |
SDL_EnableUNICODE(1); | |
#if (defined(unix) || defined(__unix__) || defined(_AIX) || defined(__OpenBSD__)) && \ | |
(!defined(__CYGWIN32__) && !defined(ENABLE_NANOX) && \ | |
!defined(__QNXNTO__) && !defined(__AROS__)) | |
// Initialise crash handler | |
struct sigaction act; | |
struct sigaction oldAct; | |
memset(&act, 0, sizeof(act)); | |
act.sa_handler = crashHandler; | |
act.sa_flags = SA_RESETHAND; | |
sigaction(SIGTERM | SIGILL | SIGABRT | SIGFPE | SIGSEGV, &act, &oldAct); | |
sigaction(SIGILL, &act, &oldAct); | |
sigaction(SIGABRT, &act, &oldAct); | |
sigaction(SIGFPE, &act, &oldAct); | |
sigaction(SIGSEGV, &act, &oldAct); | |
#endif | |
#if defined(HAVE_X11) && !defined(__QTOPIA__) | |
SDL_SysWMinfo info; | |
SDL_VERSION(&info.version); | |
if ( SDL_GetWMInfo(&info) && info.subsystem == SDL_SYSWM_X11) | |
isX11 = true; // Used in SDL_KeyTranslation.cpp | |
#endif | |
SDL_WM_SetCaption("Loading MilkyTracker...", "MilkyTracker"); | |
// ------------ initialise tracker --------------- | |
myTracker = new Tracker(); | |
PPSize windowSize = myTracker->getWindowSizeFromDatabase(); | |
if (!fullScreen) | |
fullScreen = myTracker->getFullScreenFlagFromDatabase(); | |
pp_int32 scaleFactor = myTracker->getScreenScaleFactorFromDatabase(); | |
#ifdef __LOWRES__ | |
windowSize.width = DISPLAYDEVICE_WIDTH; | |
windowSize.height = DISPLAYDEVICE_HEIGHT; | |
#endif | |
#ifdef __OPENGL__ | |
myDisplayDevice = new PPDisplayDeviceOGL(screen, windowSize.width, windowSize.height, 1, bpp, fullScreen, orientation, swapRedBlue); | |
#else | |
myDisplayDevice = new PPDisplayDeviceFB(screen, windowSize.width, windowSize.height, scaleFactor, | |
bpp, fullScreen, orientation, swapRedBlue); | |
#endif | |
myDisplayDevice->init(); | |
myTrackerScreen = new PPScreen(myDisplayDevice, myTracker); | |
myTracker->setScreen(myTrackerScreen); | |
#ifdef __QTOPIA__ | |
// On Qtopia I have to run a short event loop | |
// until drawing/blitting can be performed | |
// so the splash screen will be visible | |
for (pp_int32 i = 0; i < 500; i++) | |
{ | |
SDL_Event event; | |
SDL_PollEvent(&event); | |
} | |
#endif | |
// Startup procedure | |
myTracker->startUp(noSplash); | |
#ifdef HAVE_LIBASOUND | |
InitMidi(); | |
#endif | |
// try to create timer | |
SDL_SetTimer(20, timerCallback); | |
timerMutex->lock(); | |
ticking = true; | |
timerMutex->unlock(); | |
} | |
static bool done; | |
void exitSDLEventLoop(bool serializedEventInvoked/* = true*/) | |
{ | |
PPEvent event(eAppQuit); | |
RaiseEventSerialized(&event); | |
// it's necessary to make this mutex lock because the SDL modal event loop | |
// used in the modal dialogs expects modal dialogs to be invoked by | |
// events within these mutex lock calls | |
if (!serializedEventInvoked) | |
globalMutex->lock(); | |
bool res = myTracker->shutDown(); | |
if (!serializedEventInvoked) | |
globalMutex->unlock(); | |
if (res) | |
done = 1; | |
} | |
void SendFile(char *file) | |
{ | |
PPSystemString finalFile(file); | |
PPSystemString* strPtr = &finalFile; | |
PPEvent event(eFileDragDropped, &strPtr, sizeof(PPSystemString*)); | |
RaiseEventSerialized(&event); | |
} | |
#if defined(__PSP__) | |
extern "C" int SDL_main(int argc, char *argv[]) | |
#else | |
int main(int argc, char *argv[]) | |
#endif | |
{ | |
Uint32 videoflags; | |
SDL_Event event; | |
char *loadFile = 0; | |
pp_int32 defaultBPP = -1; | |
PPDisplayDevice::Orientations orientation = PPDisplayDevice::ORIENTATION_NORMAL; | |
bool swapRedBlue = false, fullScreen = false, noSplash = false; | |
bool recVelocity = false; | |
// Parse command line | |
while ( argc > 1 ) | |
{ | |
--argc; | |
if ( strcmp(argv[argc-1], "-bpp") == 0 ) | |
{ | |
defaultBPP = atoi(argv[argc]); | |
--argc; | |
} | |
else if ( strcmp(argv[argc], "-nosplash") == 0 ) | |
{ | |
noSplash = true; | |
} | |
else if ( strcmp(argv[argc], "-swap") == 0 ) | |
{ | |
swapRedBlue = true; | |
} | |
else if ( strcmp(argv[argc], "-fullscreen") == 0) | |
{ | |
fullScreen = true; | |
} | |
else if ( strcmp(argv[argc-1], "-orientation") == 0 ) | |
{ | |
if (strcmp(argv[argc], "NORMAL") == 0) | |
{ | |
orientation = PPDisplayDevice::ORIENTATION_NORMAL; | |
} | |
else if (strcmp(argv[argc], "ROTATE90CCW") == 0) | |
{ | |
orientation = PPDisplayDevice::ORIENTATION_ROTATE90CCW; | |
} | |
else if (strcmp(argv[argc], "ROTATE90CW") == 0) | |
{ | |
orientation = PPDisplayDevice::ORIENTATION_ROTATE90CW; | |
} | |
else | |
goto unrecognizedCommandLineSwitch; | |
--argc; | |
} | |
else if ( strcmp(argv[argc], "-nonstdkb") == 0) | |
{ | |
stdKb = false; | |
} | |
else if ( strcmp(argv[argc], "-recvelocity") == 0) | |
{ | |
recVelocity = true; | |
} | |
else | |
{ | |
unrecognizedCommandLineSwitch: | |
if (argv[argc][0] == '-') | |
{ | |
fprintf(stderr, | |
"Usage: %s [-bpp N] [-swap] [-orientation NORMAL|ROTATE90CCW|ROTATE90CW] [-fullscreen] [-nosplash] [-nonstdkb] [-recvelocity]\n", argv[0]); | |
exit(1); | |
} | |
else | |
{ | |
loadFile = argv[argc]; | |
} | |
} | |
} | |
// Workaround for seg-fault in SDL_Init on Eee PC (thanks nostromo) | |
// (see http://forum.eeeuser.com/viewtopic.php?pid=136945) | |
#if HAVE_DECL_SDL_PUTENV | |
SDL_putenv("SDL_VIDEO_X11_WMCLASS=Milkytracker"); | |
#endif | |
timerMutex = new PPMutex(); | |
globalMutex = new PPMutex(); | |
// Store current working path (init routine is likely to change it) | |
PPPath_POSIX path; | |
PPSystemString oldCwd = path.getCurrent(); | |
globalMutex->lock(); | |
initTracker(defaultBPP, orientation, swapRedBlue, fullScreen, noSplash); | |
globalMutex->unlock(); | |
#ifdef HAVE_LIBASOUND | |
if (myMidiReceiver && recVelocity) | |
{ | |
myMidiReceiver->setRecordVelocity(true); | |
} | |
#endif | |
if (loadFile) | |
{ | |
PPSystemString newCwd = path.getCurrent(); | |
path.change(oldCwd); | |
SendFile(loadFile); | |
path.change(newCwd); | |
pp_uint16 chr[3] = {VK_RETURN, 0, 0}; | |
PPEvent event(eKeyDown, &chr, sizeof(chr)); | |
RaiseEventSerialized(&event); | |
} | |
/* Main event loop */ | |
done = 0; | |
while (!done && SDL_WaitEvent(&event)) | |
{ | |
switch (event.type) | |
{ | |
case SDL_QUIT: | |
exitSDLEventLoop(false); | |
break; | |
case SDL_MOUSEMOTION: | |
{ | |
// ignore old mouse motion events in the event queue | |
SDL_Event new_event; | |
if (SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) | |
{ | |
while (SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0); | |
processSDLEvents(new_event); | |
} | |
else | |
{ | |
processSDLEvents(event); | |
} | |
break; | |
} | |
case SDL_USEREVENT: | |
processSDLUserEvents((const SDL_UserEvent&)event); | |
break; | |
default: | |
processSDLEvents(event); | |
break; | |
} | |
} | |
#if defined(__GP2X__) || defined(__CAANOO__) | |
SDL_JoystickClose(0); | |
#endif | |
timerMutex->lock(); | |
ticking = false; | |
timerMutex->unlock(); | |
SDL_SetTimer(0, NULL); | |
timerMutex->lock(); | |
globalMutex->lock(); | |
#ifdef HAVE_LIBASOUND | |
delete myMidiReceiver; | |
#endif | |
delete myTracker; | |
myTracker = NULL; | |
delete myTrackerScreen; | |
myTrackerScreen = NULL; | |
delete myDisplayDevice; | |
globalMutex->unlock(); | |
timerMutex->unlock(); | |
SDL_Quit(); | |
delete globalMutex; | |
delete timerMutex; | |
/* Quoting from README.Qtopia (Application Porting Notes): | |
One thing I have noticed is that applications sometimes don't exit | |
correctly. Their icon remains in the taskbar and they tend to | |
relaunch themselves automatically. I believe this problem doesn't | |
occur if you exit your application using the exit() method. However, | |
if you end main() with 'return 0;' or so, this seems to happen. | |
beoran: Same thing seems to happen on the Caanoo. | |
*/ | |
#if defined(__QTOPIA__) || defined(__CAANOO__) | |
exit(0); | |
#else | |
return 0; | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment