check activateable() before allowing clicks (oops)
[openc2e.git] / SDLBackend.cpp
blob5ed923d4c803969a1a11003bc9dd597cc6bec87e
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 "openc2e.h"
23 #include "Engine.h"
24 #include "creaturesImage.h"
26 SDLBackend *g_backend;
28 SDLBackend::SDLBackend() {
29 networkingup = false;
31 // reasonable defaults
32 mainsurface.width = 800;
33 mainsurface.height = 600;
34 mainsurface.surface = 0;
37 int SDLBackend::idealBpp() {
38 // shadow surfaces seem to generally be faster (presumably due to overdraw), so get SDL to create one for us
39 if (engine.version == 1) return 0;
40 else return 16;
43 void SDLBackend::resizeNotify(int _w, int _h) {
44 mainsurface.width = _w;
45 mainsurface.height = _h;
46 mainsurface.surface = SDL_SetVideoMode(_w, _h, idealBpp(), SDL_RESIZABLE);
47 if (!mainsurface.surface)
48 throw creaturesException(std::string("Failed to create SDL surface due to: ") + SDL_GetError());
51 void SDLBackend::init() {
52 int init = SDL_INIT_VIDEO;
54 if (SDL_Init(init) < 0)
55 throw creaturesException(std::string("SDL error during initialization: ") + SDL_GetError());
57 std::string windowtitle;
58 if (engine.getGameName().size()) windowtitle = engine.getGameName() + " - ";
59 windowtitle += "openc2e";
60 std::string titlebar = windowtitle + " (development build)";
61 SDL_WM_SetCaption(titlebar.c_str(), windowtitle.c_str());
63 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
64 SDL_ShowCursor(false);
65 // bz2 and fuzzie both think this is the only way to get useful ascii out of SDL
66 SDL_EnableUNICODE(1);
69 int SDLBackend::networkInit() {
70 if (SDLNet_Init() < 0)
71 throw creaturesException(std::string("SDL_net error during initialization: ") + SDLNet_GetError());
72 networkingup = true;
74 listensocket = 0;
75 int listenport = 20000;
76 while ((!listensocket) && (listenport < 20050)) {
77 listenport++;
78 IPaddress ip;
80 SDLNet_ResolveHost(&ip, 0, listenport);
81 listensocket = SDLNet_TCP_Open(&ip);
84 if (!listensocket)
85 throw creaturesException(std::string("Failed to open a port to listen on."));
87 return listenport;
90 void SDLBackend::shutdown() {
91 if (networkingup && listensocket)
92 SDLNet_TCP_Close(listensocket);
93 SDLNet_Quit();
94 SDL_Quit();
97 void SDLBackend::handleEvents() {
98 if (networkingup)
99 handleNetworking();
102 void SDLBackend::handleNetworking() {
103 // handle incoming network connections
104 while (TCPsocket connection = SDLNet_TCP_Accept(listensocket)) {
105 // check this connection is coming from localhost
106 IPaddress *remote_ip = SDLNet_TCP_GetPeerAddress(connection);
107 unsigned char *rip = (unsigned char *)&remote_ip->host;
108 if ((rip[0] != 127) || (rip[1] != 0) || (rip[2] != 0) || (rip[3] != 1)) {
109 std::cout << "Someone tried connecting via non-localhost address! IP: " << (int)rip[0] << "." << (int)rip[1] << "." << (int)rip[2] << "." << (int)rip[3] << std::endl;
110 SDLNet_TCP_Close(connection);
111 continue;
114 // read the data from the socket
115 std::string data;
116 bool done = false;
117 while (!done) {
118 char buffer;
119 int i = SDLNet_TCP_Recv(connection, &buffer, 1);
120 if (i == 1) {
121 data = data + buffer;
122 // TODO: maybe we should check for rscr\n like c2e seems to
123 if ((data.size() > 3) && (data.find("rscr\n", data.size() - 5) != data.npos)) done = true;
124 } else done = true;
127 // pass the data onto the engine, and send back our response
128 std::string tosend = engine.executeNetwork(data);
129 SDLNet_TCP_Send(connection, (void *)tosend.c_str(), tosend.size());
131 // and finally, close the connection
132 SDLNet_TCP_Close(connection);
136 bool SDLBackend::pollEvent(SomeEvent &e) {
137 SDL_Event event;
138 retry:
139 if (!SDL_PollEvent(&event)) return false;
141 switch (event.type) {
142 case SDL_VIDEORESIZE:
143 resizeNotify(event.resize.w, event.resize.h);
144 e.type = eventresizewindow;
145 e.x = event.resize.w;
146 e.y = event.resize.h;
147 break;
149 case SDL_MOUSEMOTION:
150 e.type = eventmousemove;
151 e.x = event.motion.x;
152 e.y = event.motion.y;
153 e.xrel = event.motion.xrel;
154 e.yrel = event.motion.yrel;
155 e.button = 0;
156 if (event.motion.state & SDL_BUTTON(1))
157 e.button |= buttonleft;
158 if (event.motion.state & SDL_BUTTON(2))
159 e.button |= buttonmiddle;
160 if (event.motion.state & SDL_BUTTON(3))
161 e.button |= buttonright;
162 if (event.motion.state & SDL_BUTTON(4))
163 e.button |= buttonwheelup;
164 if (event.motion.state & SDL_BUTTON(5))
165 e.button |= buttonwheeldown;
166 break;
168 case SDL_MOUSEBUTTONDOWN:
169 case SDL_MOUSEBUTTONUP:
170 if (event.type == SDL_MOUSEBUTTONDOWN)
171 e.type = eventmousebuttondown;
172 else
173 e.type = eventmousebuttonup;
174 switch (event.button.button) {
175 case SDL_BUTTON_LEFT: e.button = buttonleft; break;
176 case SDL_BUTTON_RIGHT: e.button = buttonright; break;
177 case SDL_BUTTON_MIDDLE: e.button = buttonmiddle; break;
178 case SDL_BUTTON_WHEELDOWN: e.button = buttonwheeldown; break;
179 case SDL_BUTTON_WHEELUP: e.button = buttonwheelup; break;
180 default: goto retry;
182 e.x = event.button.x;
183 e.y = event.button.y;
184 break;
186 case SDL_KEYUP:
188 int key = translateKey(event.key.keysym.sym);
189 if (key != -1) {
190 e.type = eventspecialkeyup;
191 e.key = key;
192 return true;
194 goto retry;
197 case SDL_KEYDOWN:
198 if ((event.key.keysym.unicode) && ((event.key.keysym.unicode & 0xFF80) == 0) && (event.key.keysym.unicode >= 32)) {
199 e.type = eventkeydown;
200 e.key = event.key.keysym.unicode & 0x7F;
201 return true;
202 } else { // TODO: should this be 'else'?
203 int key = translateKey(event.key.keysym.sym);
204 if (key != -1) {
205 e.type = eventspecialkeydown;
206 e.key = key;
207 return true;
210 goto retry;
211 break;
213 case SDL_QUIT:
214 e.type = eventquit;
215 break;
217 default:
218 goto retry;
221 return true;
224 void SDLSurface::renderLine(int x1, int y1, int x2, int y2, unsigned int colour) {
225 aalineColor(surface, x1, y1, x2, y2, colour);
228 //*** code to mirror 16bpp surface - slow, we should cache this!
230 Uint16 *pixelPtr(SDL_Surface *surf, int x, int y) {
231 return (Uint16 *)((Uint8 *)surf->pixels + (y * surf->pitch) + (x * 2));
234 SDL_Surface *MirrorSurface(SDL_Surface *surf) {
235 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);
236 assert(newsurf);
237 SDL_BlitSurface(surf, 0, newsurf, 0);
239 if (SDL_MUSTLOCK(newsurf))
240 if (SDL_LockSurface(newsurf) == -1) {
241 SDL_FreeSurface(newsurf);
242 throw creaturesException("SDLBackend failed to lock surface for mirroring");
245 for (int y = 0; y < newsurf->h; y++) {
246 for (int x = 0; x < (newsurf->w / 2); x++) {
247 Uint16 *one = pixelPtr(newsurf, x, y);
248 Uint16 *two = pixelPtr(newsurf, (newsurf->w - 1) - x, y);
249 Uint16 temp = *one;
250 *one = *two;
251 *two = temp;
255 if (SDL_MUSTLOCK(newsurf))
256 SDL_UnlockSurface(newsurf);
258 return newsurf;
261 //*** end mirror code
263 void SDLSurface::render(shared_ptr<creaturesImage> image, unsigned int frame, int x, int y, bool trans, unsigned char transparency, bool mirror, bool is_background) {
264 assert(image);
266 // don't bother rendering off-screen stuff
267 if (x >= (int)width) return; if (y >= (int)height) return;
268 if ((x + image->width(frame)) <= 0) return;
269 if ((y + image->height(frame)) <= 0) return;
271 // create surface
272 SDL_Surface *surf;
273 if (image->format() == if_paletted) {
274 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
275 image->width(frame), image->height(frame),
276 8, // depth
277 image->width(frame), // pitch
278 0, 0, 0, 0);
279 assert(surf);
280 SDL_SetPalette(surf, SDL_LOGPAL, palette, 0, 256);
281 } else if (image->format() == if_16bit) {
282 unsigned int rmask, gmask, bmask;
283 if (image->is565()) {
284 rmask = 0xF800; gmask = 0x07E0; bmask = 0x001F;
285 } else {
286 rmask = 0x7C00; gmask = 0x03E0; bmask = 0x001F;
288 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
289 image->width(frame), image->height(frame),
290 16, // depth
291 image->width(frame) * 2, // pitch
292 rmask, gmask, bmask, 0); // RGBA mask
293 assert(surf);
294 } else {
295 assert(image->format() == if_24bit);
297 surf = SDL_CreateRGBSurfaceFrom(image->data(frame),
298 image->width(frame), image->height(frame),
299 24, // depth
300 image->width(frame) * 3, // pitch
301 0x00FF0000, 0x0000FF00, 0x000000FF, 0); // RGBA mask
302 assert(surf);
306 // try mirroring, if necessary
307 try {
308 if (mirror) {
309 SDL_Surface *newsurf = MirrorSurface(surf);
310 SDL_FreeSurface(surf);
311 surf = newsurf;
313 } catch (std::exception &e) {
314 SDL_FreeSurface(surf);
315 throw;
318 // set colour-keying/alpha
319 if (!is_background) SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
320 if (trans) SDL_SetAlpha(surf, SDL_SRCALPHA, 255 - transparency);
322 // do actual blit
323 SDL_Rect destrect;
324 destrect.x = x; destrect.y = y;
325 SDL_BlitSurface(surf, 0, surface, &destrect);
327 // free surface
328 SDL_FreeSurface(surf);
331 void SDLSurface::renderDone() {
332 SDL_Flip(surface);
335 void SDLSurface::blitSurface(Surface *s, int x, int y, int w, int h) {
336 SDLSurface *src = dynamic_cast<SDLSurface *>(s);
337 assert(src);
339 // TODO: evil use of internal SDL api
340 SDL_Rect r; r.x = x; r.y = y; r.w = w; r.h = h;
341 SDL_SoftStretch(src->surface, 0, surface, &r);
344 Surface *SDLBackend::newSurface(unsigned int w, unsigned int h) {
345 SDL_Surface *surf = mainsurface.surface;
346 SDL_Surface* underlyingsurf = SDL_CreateRGBSurface(SDL_HWSURFACE, w, h, surf->format->BitsPerPixel, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
347 assert(underlyingsurf);
348 SDLSurface *newsurf = new SDLSurface();
349 newsurf->surface = underlyingsurf;
350 newsurf->width = w;
351 newsurf->height = h;
352 return newsurf;
355 void SDLBackend::freeSurface(Surface *s) {
356 SDLSurface *surf = dynamic_cast<SDLSurface *>(s);
357 assert(surf);
359 SDL_FreeSurface(surf->surface);
360 delete surf;
363 // left out: menu, select, execute, snapshot, numeric keypad, f keys
364 #define keytrans_size 25
365 struct _keytrans { int sdl, windows; } keytrans[keytrans_size] = {
366 { SDLK_BACKSPACE, 8 },
367 { SDLK_TAB, 9 },
368 { SDLK_CLEAR, 12 },
369 { SDLK_RETURN, 13 },
370 { SDLK_RSHIFT, 16 },
371 { SDLK_LSHIFT, 16 },
372 { SDLK_RCTRL, 17 },
373 { SDLK_LCTRL, 17 },
374 { SDLK_PAUSE, 19 },
375 { SDLK_CAPSLOCK, 20 },
376 { SDLK_ESCAPE, 27 },
377 { SDLK_SPACE, 32 },
378 { SDLK_PAGEUP, 33 },
379 { SDLK_PAGEDOWN, 34 },
380 { SDLK_END, 35 },
381 { SDLK_HOME, 36 },
382 { SDLK_LEFT, 37 },
383 { SDLK_UP, 38 },
384 { SDLK_RIGHT, 39 },
385 { SDLK_DOWN, 40 },
386 { SDLK_PRINT, 42 },
387 { SDLK_INSERT, 45 },
388 { SDLK_DELETE, 46 },
389 { SDLK_NUMLOCK, 144 }
392 // TODO: handle f keys (112-123 under windows, SDLK_F1 = 282 under sdl)
394 // TODO: this is possibly not a great idea, we should maybe maintain our own state table
395 bool SDLBackend::keyDown(int key) {
396 Uint8 *keystate = SDL_GetKeyState(NULL);
398 for (unsigned int i = 0; i < keytrans_size; i++) {
399 if (keytrans[i].windows == key)
400 if (keystate[keytrans[i].sdl])
401 return true;
404 return false;
407 int SDLBackend::translateKey(int key) {
408 if (key >= 97 && key <= 122) { // lowercase letters
409 return key - 32; // capitalise
411 if (key >= 48 && key <= 57) { // numbers
412 return key;
415 for (unsigned int i = 0; i < keytrans_size; i++) {
416 if (keytrans[i].sdl == key)
417 return keytrans[i].windows;
420 return -1;
423 void SDLBackend::setPalette(uint8 *data) {
424 // TODO: we only set the palette on our main surface, so will fail for any C1 cameras!
425 for (unsigned int i = 0; i < 256; i++) {
426 mainsurface.palette[i].r = data[i * 3];
427 mainsurface.palette[i].g = data[(i * 3) + 1];
428 mainsurface.palette[i].b = data[(i * 3) + 2];
432 void SDLBackend::delay(int msec) {
433 SDL_Delay(msec);