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"
25 #include "creaturesImage.h"
27 SDLBackend
*g_backend
;
29 SDLBackend::SDLBackend() : mainsurface(this) {
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;
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
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());
84 int listenport
= 20000;
85 while ((!listensocket
) && (listenport
< 20050)) {
89 SDLNet_ResolveHost(&ip
, 0, listenport
);
90 listensocket
= SDLNet_TCP_Open(&ip
);
94 throw creaturesException(std::string("Failed to open a port to listen on."));
99 void SDLBackend::shutdown() {
102 if (networkingup
&& listensocket
)
103 SDLNet_TCP_Close(listensocket
);
108 void SDLBackend::handleEvents() {
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
);
125 // read the data from the socket
130 int i
= SDLNet_TCP_Recv(connection
, &buffer
, 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;
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
) {
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
;
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
;
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
;
179 case SDL_MOUSEBUTTONDOWN
:
180 case SDL_MOUSEBUTTONUP
:
181 if (event
.type
== SDL_MOUSEBUTTONDOWN
)
182 e
.type
= eventmousebuttondown
;
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;
193 e
.x
= event
.button
.x
;
194 e
.y
= event
.button
.y
;
199 int key
= translateKey(event
.key
.keysym
.sym
);
201 e
.type
= eventspecialkeyup
;
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;
213 } else { // TODO: should this be 'else'?
214 int key
= translateKey(event
.key
.keysym
.sym
);
216 e
.type
= eventspecialkeydown
;
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
243 sdlc
.g
= (c
>> 8) & 0xff;
244 sdlc
.r
= (c
>> 16) & 0xff;
245 assert(c
>> 24 == 0);
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;
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
);
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
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
);
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
);
303 if (SDL_MUSTLOCK(newsurf
))
304 SDL_UnlockSurface(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
) {
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;
321 if (image
->format() == if_paletted
) {
322 surf
= SDL_CreateRGBSurfaceFrom(image
->data(frame
),
323 image
->width(frame
), image
->height(frame
),
325 image
->width(frame
), // pitch
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;
334 rmask
= 0x7C00; gmask
= 0x03E0; bmask
= 0x001F;
336 surf
= SDL_CreateRGBSurfaceFrom(image
->data(frame
),
337 image
->width(frame
), image
->height(frame
),
339 image
->width(frame
) * 2, // pitch
340 rmask
, gmask
, bmask
, 0); // RGBA mask
343 assert(image
->format() == if_24bit
);
345 surf
= SDL_CreateRGBSurfaceFrom(image
->data(frame
),
346 image
->width(frame
), image
->height(frame
),
348 image
->width(frame
) * 3, // pitch
349 0x00FF0000, 0x0000FF00, 0x000000FF, 0); // RGBA mask
354 // try mirroring, if necessary
357 SDL_Surface
*newsurf
= MirrorSurface(surf
);
358 SDL_FreeSurface(surf
);
361 } catch (std::exception
&e
) {
362 SDL_FreeSurface(surf
);
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
);
372 destrect
.x
= x
; destrect
.y
= y
;
373 SDL_BlitSurface(surf
, 0, surface
, &destrect
);
376 SDL_FreeSurface(surf
);
379 void SDLSurface::renderDone() {
383 void SDLSurface::blitSurface(Surface
*s
, int x
, int y
, int w
, int h
) {
384 SDLSurface
*src
= dynamic_cast<SDLSurface
*>(s
);
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
;
403 void SDLBackend::freeSurface(Surface
*s
) {
404 SDLSurface
*surf
= dynamic_cast<SDLSurface
*>(s
);
407 SDL_FreeSurface(surf
->surface
);
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 },
423 { SDLK_CAPSLOCK
, 20 },
427 { SDLK_PAGEDOWN
, 34 },
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
])
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
463 for (unsigned int i
= 0; i
< keytrans_size
; i
++) {
464 if (keytrans
[i
].sdl
== key
)
465 return keytrans
[i
].windows
;
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
) {