Skip to content

Instantly share code, notes, and snippets.

@beoran
Created July 19, 2011 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beoran/1093843 to your computer and use it in GitHub Desktop.
Save beoran/1093843 to your computer and use it in GitHub Desktop.
Milkytracker CANOO
/*
* 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