fix agentOnCamera to cope with the wrap
[openc2e.git] / SDLBackend.cpp
blob83422b74daee060d5b166f12563141a32aea12c5
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;
251 if (text.empty()) return;
253 SDL_Color sdlcolour;
254 if (engine.version == 1) sdlcolour = palette[colour];
255 else sdlcolour = getColourFromRGBA(colour);
257 SDL_Surface *textsurf;
259 if (bgcolour == 0) { // transparent
260 textsurf = TTF_RenderText_Solid(parent->basicfont, text.c_str(), sdlcolour);
261 } else {
262 SDL_Color sdlbgcolour;
263 if (engine.version == 1) sdlbgcolour = palette[bgcolour];
264 else sdlbgcolour = getColourFromRGBA(bgcolour);
265 textsurf = TTF_RenderText_Shaded(parent->basicfont, text.c_str(), sdlcolour, sdlbgcolour);
268 if (!textsurf) return; // thanks, SDL_ttf, we love you too
270 SDL_Rect destrect;
271 destrect.x = x; destrect.y = y;
272 SDL_BlitSurface(textsurf, NULL, surface, &destrect);
273 SDL_FreeSurface(textsurf);
276 //*** code to mirror 16bpp surface - slow, we should cache this!
278 Uint16 *pixelPtr(SDL_Surface *surf, int x, int y) {
279 return (Uint16 *)((Uint8 *)surf->pixels + (y * surf->pitch) + (x * 2));
282 SDL_Surface *MirrorSurface(SDL_Surface *surf) {
283 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);
284 assert(newsurf);
285 SDL_BlitSurface(surf, 0, newsurf, 0);
287 if (SDL_MUSTLOCK(newsurf))
288 if (SDL_LockSurface(newsurf) == -1) {
289 SDL_FreeSurface(newsurf);
290 throw creaturesException("SDLBackend failed to lock surface for mirroring");
293 for (int y = 0; y < newsurf->h; y++) {
294 for (int x = 0; x < (newsurf->w / 2); x++) {
295 Uint16 *one = pixelPtr(newsurf, x, y);
296 Uint16 *two = pixelPtr(newsurf, (newsurf->w - 1) - x, y);
297 Uint16 temp = *one;
298 *one = *two;
299 *two = temp;
303 if (SDL_MUSTLOCK(newsurf))
304 SDL_UnlockSurface(newsurf);
306 return newsurf;
309 //*** end mirror code
311 void SDLSurface::render(shared_ptr<creaturesImage> image, unsigned int frame, int x, int y, bool trans, unsigned char transparency, bool mirror, bool is_background) {
312 assert(image);
314 // don't bother rendering off-screen stuff
315 if (x >= (int)width) return; if (y >= (int)height) return;
316 if ((x + image->width(frame)) <= 0) return;
317 if ((y + image->height(frame)) <= 0) return;
319 // create surface
320 SDL_Surface *surf;
321 if (image->format() == if_paletted) {
322 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
323 image->width(frame), image->height(frame),
324 8, // depth
325 image->width(frame), // pitch
326 0, 0, 0, 0);
327 assert(surf);
328 SDL_SetPalette(surf, SDL_LOGPAL, palette, 0, 256);
329 } else if (image->format() == if_16bit) {
330 unsigned int rmask, gmask, bmask;
331 if (image->is565()) {
332 rmask = 0xF800; gmask = 0x07E0; bmask = 0x001F;
333 } else {
334 rmask = 0x7C00; gmask = 0x03E0; bmask = 0x001F;
336 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
337 image->width(frame), image->height(frame),
338 16, // depth
339 image->width(frame) * 2, // pitch
340 rmask, gmask, bmask, 0); // RGBA mask
341 assert(surf);
342 } else {
343 assert(image->format() == if_24bit);
345 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
346 image->width(frame), image->height(frame),
347 24, // depth
348 image->width(frame) * 3, // pitch
349 0x00FF0000, 0x0000FF00, 0x000000FF, 0); // RGBA mask
350 assert(surf);
354 // try mirroring, if necessary
355 try {
356 if (mirror) {
357 SDL_Surface *newsurf = MirrorSurface(surf);
358 SDL_FreeSurface(surf);
359 surf = newsurf;
361 } catch (std::exception &e) {
362 SDL_FreeSurface(surf);
363 throw;
366 // set colour-keying/alpha
367 if (!is_background) SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
368 if (trans) SDL_SetAlpha(surf, SDL_SRCALPHA, 255 - transparency);
370 // do actual blit
371 SDL_Rect destrect;
372 destrect.x = x; destrect.y = y;
373 SDL_BlitSurface(surf, 0, surface, &destrect);
375 // free surface
376 SDL_FreeSurface(surf);
379 void SDLSurface::renderDone() {
380 SDL_Flip(surface);
383 void SDLSurface::blitSurface(Surface *s, int x, int y, int w, int h) {
384 SDLSurface *src = dynamic_cast<SDLSurface *>(s);
385 assert(src);
387 // TODO: evil use of internal SDL api
388 SDL_Rect r; r.x = x; r.y = y; r.w = w; r.h = h;
389 SDL_SoftStretch(src->surface, 0, surface, &r);
392 Surface *SDLBackend::newSurface(unsigned int w, unsigned int h) {
393 SDL_Surface *surf = mainsurface.surface;
394 SDL_Surface* underlyingsurf = SDL_CreateRGBSurface(SDL_HWSURFACE, w, h, surf->format->BitsPerPixel, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
395 assert(underlyingsurf);
396 SDLSurface *newsurf = new SDLSurface(this);
397 newsurf->surface = underlyingsurf;
398 newsurf->width = w;
399 newsurf->height = h;
400 return newsurf;
403 void SDLBackend::freeSurface(Surface *s) {
404 SDLSurface *surf = dynamic_cast<SDLSurface *>(s);
405 assert(surf);
407 SDL_FreeSurface(surf->surface);
408 delete surf;
411 // left out: menu, select, execute, snapshot, numeric keypad, f keys
412 #define keytrans_size 25
413 struct _keytrans { int sdl, windows; } keytrans[keytrans_size] = {
414 { SDLK_BACKSPACE, 8 },
415 { SDLK_TAB, 9 },
416 { SDLK_CLEAR, 12 },
417 { SDLK_RETURN, 13 },
418 { SDLK_RSHIFT, 16 },
419 { SDLK_LSHIFT, 16 },
420 { SDLK_RCTRL, 17 },
421 { SDLK_LCTRL, 17 },
422 { SDLK_PAUSE, 19 },
423 { SDLK_CAPSLOCK, 20 },
424 { SDLK_ESCAPE, 27 },
425 { SDLK_SPACE, 32 },
426 { SDLK_PAGEUP, 33 },
427 { SDLK_PAGEDOWN, 34 },
428 { SDLK_END, 35 },
429 { SDLK_HOME, 36 },
430 { SDLK_LEFT, 37 },
431 { SDLK_UP, 38 },
432 { SDLK_RIGHT, 39 },
433 { SDLK_DOWN, 40 },
434 { SDLK_PRINT, 42 },
435 { SDLK_INSERT, 45 },
436 { SDLK_DELETE, 46 },
437 { SDLK_NUMLOCK, 144 }
440 // TODO: handle f keys (112-123 under windows, SDLK_F1 = 282 under sdl)
442 // TODO: this is possibly not a great idea, we should maybe maintain our own state table
443 bool SDLBackend::keyDown(int key) {
444 Uint8 *keystate = SDL_GetKeyState(NULL);
446 for (unsigned int i = 0; i < keytrans_size; i++) {
447 if (keytrans[i].windows == key)
448 if (keystate[keytrans[i].sdl])
449 return true;
452 return false;
455 int SDLBackend::translateKey(int key) {
456 if (key >= 97 && key <= 122) { // lowercase letters
457 return key - 32; // capitalise
459 if (key >= 48 && key <= 57) { // numbers
460 return key;
463 for (unsigned int i = 0; i < keytrans_size; i++) {
464 if (keytrans[i].sdl == key)
465 return keytrans[i].windows;
468 return -1;
471 void SDLBackend::setPalette(uint8 *data) {
472 // TODO: we only set the palette on our main surface, so will fail for any C1 cameras!
473 for (unsigned int i = 0; i < 256; i++) {
474 mainsurface.palette[i].r = data[i * 3];
475 mainsurface.palette[i].g = data[(i * 3) + 1];
476 mainsurface.palette[i].b = data[(i * 3) + 2];
480 void SDLBackend::delay(int msec) {
481 SDL_Delay(msec);