read Blackboard data correctly from the SFC file, and construct a BlackboardPart...
[openc2e.git] / SDLBackend.cpp
blob3fec2467699d882ffea81aeaa3bc1cbb830b8b06
1 /*
2 * SDLBackend.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Sun Oct 24 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "SDLBackend.h"
21 #include "SDL_gfxPrimitives.h"
22 #include "SDL_ttf.h"
23 #include "openc2e.h"
24 #include "Engine.h"
25 #include "creaturesImage.h"
27 SDLBackend *g_backend;
29 SDLBackend::SDLBackend() : mainsurface(this) {
30 networkingup = false;
31 basicfont = 0;
33 // reasonable defaults
34 mainsurface.width = 800;
35 mainsurface.height = 600;
36 mainsurface.surface = 0;
39 int SDLBackend::idealBpp() {
40 // shadow surfaces seem to generally be faster (presumably due to overdraw), so get SDL to create one for us
41 if (engine.version == 1) return 0;
42 else return 16;
45 void SDLBackend::resizeNotify(int _w, int _h) {
46 mainsurface.width = _w;
47 mainsurface.height = _h;
48 mainsurface.surface = SDL_SetVideoMode(_w, _h, idealBpp(), SDL_RESIZABLE);
49 if (!mainsurface.surface)
50 throw creaturesException(std::string("Failed to create SDL surface due to: ") + SDL_GetError());
53 void SDLBackend::init() {
54 int init = SDL_INIT_VIDEO;
56 if (SDL_Init(init) < 0)
57 throw creaturesException(std::string("SDL error during initialization: ") + SDL_GetError());
59 std::string windowtitle;
60 if (engine.getGameName().size()) windowtitle = engine.getGameName() + " - ";
61 windowtitle += "openc2e";
62 std::string titlebar = windowtitle + " (development build)";
63 SDL_WM_SetCaption(titlebar.c_str(), windowtitle.c_str());
65 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
66 SDL_ShowCursor(false);
67 // bz2 and fuzzie both think this is the only way to get useful ascii out of SDL
68 SDL_EnableUNICODE(1);
70 if (TTF_Init() == 0) {
71 // TODO: think about font sizing
72 basicfont = TTF_OpenFont("VeraSe.ttf", 9);
73 if (!basicfont) // TODO: think about font fallbacks/etc
74 basicfont = TTF_OpenFont("/usr/share/fonts/truetype/ttf-bitstream-vera/VeraSe.ttf", 9);
78 int SDLBackend::networkInit() {
79 if (SDLNet_Init() < 0)
80 throw creaturesException(std::string("SDL_net error during initialization: ") + SDLNet_GetError());
81 networkingup = true;
83 listensocket = 0;
84 int listenport = 20000;
85 while ((!listensocket) && (listenport < 20050)) {
86 listenport++;
87 IPaddress ip;
89 SDLNet_ResolveHost(&ip, 0, listenport);
90 listensocket = SDLNet_TCP_Open(&ip);
93 if (!listensocket)
94 throw creaturesException(std::string("Failed to open a port to listen on."));
96 return listenport;
99 void SDLBackend::shutdown() {
100 if (TTF_WasInit())
101 TTF_Quit();
102 if (networkingup && listensocket)
103 SDLNet_TCP_Close(listensocket);
104 SDLNet_Quit();
105 SDL_Quit();
108 void SDLBackend::handleEvents() {
109 if (networkingup)
110 handleNetworking();
113 void SDLBackend::handleNetworking() {
114 // handle incoming network connections
115 while (TCPsocket connection = SDLNet_TCP_Accept(listensocket)) {
116 // check this connection is coming from localhost
117 IPaddress *remote_ip = SDLNet_TCP_GetPeerAddress(connection);
118 unsigned char *rip = (unsigned char *)&remote_ip->host;
119 if ((rip[0] != 127) || (rip[1] != 0) || (rip[2] != 0) || (rip[3] != 1)) {
120 std::cout << "Someone tried connecting via non-localhost address! IP: " << (int)rip[0] << "." << (int)rip[1] << "." << (int)rip[2] << "." << (int)rip[3] << std::endl;
121 SDLNet_TCP_Close(connection);
122 continue;
125 // read the data from the socket
126 std::string data;
127 bool done = false;
128 while (!done) {
129 char buffer;
130 int i = SDLNet_TCP_Recv(connection, &buffer, 1);
131 if (i == 1) {
132 data = data + buffer;
133 // TODO: maybe we should check for rscr\n like c2e seems to
134 if ((data.size() > 3) && (data.find("rscr\n", data.size() - 5) != data.npos)) done = true;
135 } else done = true;
138 // pass the data onto the engine, and send back our response
139 std::string tosend = engine.executeNetwork(data);
140 SDLNet_TCP_Send(connection, (void *)tosend.c_str(), tosend.size());
142 // and finally, close the connection
143 SDLNet_TCP_Close(connection);
147 bool SDLBackend::pollEvent(SomeEvent &e) {
148 SDL_Event event;
149 retry:
150 if (!SDL_PollEvent(&event)) return false;
152 switch (event.type) {
153 case SDL_VIDEORESIZE:
154 resizeNotify(event.resize.w, event.resize.h);
155 e.type = eventresizewindow;
156 e.x = event.resize.w;
157 e.y = event.resize.h;
158 break;
160 case SDL_MOUSEMOTION:
161 e.type = eventmousemove;
162 e.x = event.motion.x;
163 e.y = event.motion.y;
164 e.xrel = event.motion.xrel;
165 e.yrel = event.motion.yrel;
166 e.button = 0;
167 if (event.motion.state & SDL_BUTTON(1))
168 e.button |= buttonleft;
169 if (event.motion.state & SDL_BUTTON(2))
170 e.button |= buttonmiddle;
171 if (event.motion.state & SDL_BUTTON(3))
172 e.button |= buttonright;
173 if (event.motion.state & SDL_BUTTON(4))
174 e.button |= buttonwheelup;
175 if (event.motion.state & SDL_BUTTON(5))
176 e.button |= buttonwheeldown;
177 break;
179 case SDL_MOUSEBUTTONDOWN:
180 case SDL_MOUSEBUTTONUP:
181 if (event.type == SDL_MOUSEBUTTONDOWN)
182 e.type = eventmousebuttondown;
183 else
184 e.type = eventmousebuttonup;
185 switch (event.button.button) {
186 case SDL_BUTTON_LEFT: e.button = buttonleft; break;
187 case SDL_BUTTON_RIGHT: e.button = buttonright; break;
188 case SDL_BUTTON_MIDDLE: e.button = buttonmiddle; break;
189 case SDL_BUTTON_WHEELDOWN: e.button = buttonwheeldown; break;
190 case SDL_BUTTON_WHEELUP: e.button = buttonwheelup; break;
191 default: goto retry;
193 e.x = event.button.x;
194 e.y = event.button.y;
195 break;
197 case SDL_KEYUP:
199 int key = translateKey(event.key.keysym.sym);
200 if (key != -1) {
201 e.type = eventspecialkeyup;
202 e.key = key;
203 return true;
205 goto retry;
208 case SDL_KEYDOWN:
209 if ((event.key.keysym.unicode) && ((event.key.keysym.unicode & 0xFF80) == 0) && (event.key.keysym.unicode >= 32)) {
210 e.type = eventkeydown;
211 e.key = event.key.keysym.unicode & 0x7F;
212 return true;
213 } else { // TODO: should this be 'else'?
214 int key = translateKey(event.key.keysym.sym);
215 if (key != -1) {
216 e.type = eventspecialkeydown;
217 e.key = key;
218 return true;
221 goto retry;
222 break;
224 case SDL_QUIT:
225 e.type = eventquit;
226 break;
228 default:
229 goto retry;
232 return true;
235 void SDLSurface::renderLine(int x1, int y1, int x2, int y2, unsigned int colour) {
236 aalineColor(surface, x1, y1, x2, y2, colour);
239 SDL_Color getColourFromRGBA(unsigned int c) {
240 // SDL's functions seem to want a pixelformat, which is more effort to fake than just doing this
241 SDL_Color sdlc;
242 sdlc.b = c & 0xff;
243 sdlc.g = (c >> 8) & 0xff;
244 sdlc.r = (c >> 16) & 0xff;
245 assert(c >> 24 == 0);
246 return sdlc;
249 void SDLSurface::renderText(int x, int y, std::string text, unsigned int colour, unsigned int bgcolour) {
250 if (!parent->basicfont) return;
252 SDL_Color sdlcolour;
253 if (engine.version == 1) sdlcolour = palette[colour];
254 else sdlcolour = getColourFromRGBA(colour);
256 SDL_Surface *textsurf;
258 if (bgcolour == 0) { // transparent
259 textsurf = TTF_RenderText_Solid(parent->basicfont, text.c_str(), sdlcolour);
260 } else {
261 SDL_Color sdlbgcolour;
262 if (engine.version == 1) sdlbgcolour = palette[bgcolour];
263 else sdlbgcolour = getColourFromRGBA(bgcolour);
264 textsurf = TTF_RenderText_Shaded(parent->basicfont, text.c_str(), sdlcolour, sdlbgcolour);
267 assert(textsurf);
269 SDL_Rect destrect;
270 destrect.x = x; destrect.y = y;
271 SDL_BlitSurface(textsurf, NULL, surface, &destrect);
272 SDL_FreeSurface(textsurf);
275 //*** code to mirror 16bpp surface - slow, we should cache this!
277 Uint16 *pixelPtr(SDL_Surface *surf, int x, int y) {
278 return (Uint16 *)((Uint8 *)surf->pixels + (y * surf->pitch) + (x * 2));
281 SDL_Surface *MirrorSurface(SDL_Surface *surf) {
282 SDL_Surface* newsurf = SDL_CreateRGBSurface(SDL_HWSURFACE, surf->w, surf->h, surf->format->BitsPerPixel, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
283 assert(newsurf);
284 SDL_BlitSurface(surf, 0, newsurf, 0);
286 if (SDL_MUSTLOCK(newsurf))
287 if (SDL_LockSurface(newsurf) == -1) {
288 SDL_FreeSurface(newsurf);
289 throw creaturesException("SDLBackend failed to lock surface for mirroring");
292 for (int y = 0; y < newsurf->h; y++) {
293 for (int x = 0; x < (newsurf->w / 2); x++) {
294 Uint16 *one = pixelPtr(newsurf, x, y);
295 Uint16 *two = pixelPtr(newsurf, (newsurf->w - 1) - x, y);
296 Uint16 temp = *one;
297 *one = *two;
298 *two = temp;
302 if (SDL_MUSTLOCK(newsurf))
303 SDL_UnlockSurface(newsurf);
305 return newsurf;
308 //*** end mirror code
310 void SDLSurface::render(shared_ptr<creaturesImage> image, unsigned int frame, int x, int y, bool trans, unsigned char transparency, bool mirror, bool is_background) {
311 assert(image);
313 // don't bother rendering off-screen stuff
314 if (x >= (int)width) return; if (y >= (int)height) return;
315 if ((x + image->width(frame)) <= 0) return;
316 if ((y + image->height(frame)) <= 0) return;
318 // create surface
319 SDL_Surface *surf;
320 if (image->format() == if_paletted) {
321 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
322 image->width(frame), image->height(frame),
323 8, // depth
324 image->width(frame), // pitch
325 0, 0, 0, 0);
326 assert(surf);
327 SDL_SetPalette(surf, SDL_LOGPAL, palette, 0, 256);
328 } else if (image->format() == if_16bit) {
329 unsigned int rmask, gmask, bmask;
330 if (image->is565()) {
331 rmask = 0xF800; gmask = 0x07E0; bmask = 0x001F;
332 } else {
333 rmask = 0x7C00; gmask = 0x03E0; bmask = 0x001F;
335 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
336 image->width(frame), image->height(frame),
337 16, // depth
338 image->width(frame) * 2, // pitch
339 rmask, gmask, bmask, 0); // RGBA mask
340 assert(surf);
341 } else {
342 assert(image->format() == if_24bit);
344 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
345 image->width(frame), image->height(frame),
346 24, // depth
347 image->width(frame) * 3, // pitch
348 0x00FF0000, 0x0000FF00, 0x000000FF, 0); // RGBA mask
349 assert(surf);
353 // try mirroring, if necessary
354 try {
355 if (mirror) {
356 SDL_Surface *newsurf = MirrorSurface(surf);
357 SDL_FreeSurface(surf);
358 surf = newsurf;
360 } catch (std::exception &e) {
361 SDL_FreeSurface(surf);
362 throw;
365 // set colour-keying/alpha
366 if (!is_background) SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
367 if (trans) SDL_SetAlpha(surf, SDL_SRCALPHA, 255 - transparency);
369 // do actual blit
370 SDL_Rect destrect;
371 destrect.x = x; destrect.y = y;
372 SDL_BlitSurface(surf, 0, surface, &destrect);
374 // free surface
375 SDL_FreeSurface(surf);
378 void SDLSurface::renderDone() {
379 SDL_Flip(surface);
382 void SDLSurface::blitSurface(Surface *s, int x, int y, int w, int h) {
383 SDLSurface *src = dynamic_cast<SDLSurface *>(s);
384 assert(src);
386 // TODO: evil use of internal SDL api
387 SDL_Rect r; r.x = x; r.y = y; r.w = w; r.h = h;
388 SDL_SoftStretch(src->surface, 0, surface, &r);
391 Surface *SDLBackend::newSurface(unsigned int w, unsigned int h) {
392 SDL_Surface *surf = mainsurface.surface;
393 SDL_Surface* underlyingsurf = SDL_CreateRGBSurface(SDL_HWSURFACE, w, h, surf->format->BitsPerPixel, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
394 assert(underlyingsurf);
395 SDLSurface *newsurf = new SDLSurface(this);
396 newsurf->surface = underlyingsurf;
397 newsurf->width = w;
398 newsurf->height = h;
399 return newsurf;
402 void SDLBackend::freeSurface(Surface *s) {
403 SDLSurface *surf = dynamic_cast<SDLSurface *>(s);
404 assert(surf);
406 SDL_FreeSurface(surf->surface);
407 delete surf;
410 // left out: menu, select, execute, snapshot, numeric keypad, f keys
411 #define keytrans_size 25
412 struct _keytrans { int sdl, windows; } keytrans[keytrans_size] = {
413 { SDLK_BACKSPACE, 8 },
414 { SDLK_TAB, 9 },
415 { SDLK_CLEAR, 12 },
416 { SDLK_RETURN, 13 },
417 { SDLK_RSHIFT, 16 },
418 { SDLK_LSHIFT, 16 },
419 { SDLK_RCTRL, 17 },
420 { SDLK_LCTRL, 17 },
421 { SDLK_PAUSE, 19 },
422 { SDLK_CAPSLOCK, 20 },
423 { SDLK_ESCAPE, 27 },
424 { SDLK_SPACE, 32 },
425 { SDLK_PAGEUP, 33 },
426 { SDLK_PAGEDOWN, 34 },
427 { SDLK_END, 35 },
428 { SDLK_HOME, 36 },
429 { SDLK_LEFT, 37 },
430 { SDLK_UP, 38 },
431 { SDLK_RIGHT, 39 },
432 { SDLK_DOWN, 40 },
433 { SDLK_PRINT, 42 },
434 { SDLK_INSERT, 45 },
435 { SDLK_DELETE, 46 },
436 { SDLK_NUMLOCK, 144 }
439 // TODO: handle f keys (112-123 under windows, SDLK_F1 = 282 under sdl)
441 // TODO: this is possibly not a great idea, we should maybe maintain our own state table
442 bool SDLBackend::keyDown(int key) {
443 Uint8 *keystate = SDL_GetKeyState(NULL);
445 for (unsigned int i = 0; i < keytrans_size; i++) {
446 if (keytrans[i].windows == key)
447 if (keystate[keytrans[i].sdl])
448 return true;
451 return false;
454 int SDLBackend::translateKey(int key) {
455 if (key >= 97 && key <= 122) { // lowercase letters
456 return key - 32; // capitalise
458 if (key >= 48 && key <= 57) { // numbers
459 return key;
462 for (unsigned int i = 0; i < keytrans_size; i++) {
463 if (keytrans[i].sdl == key)
464 return keytrans[i].windows;
467 return -1;
470 void SDLBackend::setPalette(uint8 *data) {
471 // TODO: we only set the palette on our main surface, so will fail for any C1 cameras!
472 for (unsigned int i = 0; i < 256; i++) {
473 mainsurface.palette[i].r = data[i * 3];
474 mainsurface.palette[i].g = data[(i * 3) + 1];
475 mainsurface.palette[i].b = data[(i * 3) + 2];
479 void SDLBackend::delay(int msec) {
480 SDL_Delay(msec);