26ae459fca66f6d98ddf6fff2a4fe96b05bd64ed
[nit.git] / lib / sdl.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # SDL display support (used in Linux for windows and inputes only)
18 module sdl is
19 c_compiler_option(exec("sdl-config", "--cflags"))
20 c_linker_option(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf")
21 end
22
23 import mnit_display
24
25 in "C header" `{
26 #include <unistd.h>
27 #include <SDL/SDL.h>
28 #include <SDL/SDL_syswm.h>
29 #include <SDL/SDL_image.h>
30 #include <SDL/SDL_ttf.h>
31 `}
32
33 # Represent a screen surface
34 extern class SDLDisplay `{SDL_Surface *`}
35 super Display
36
37 redef type I: SDLImage
38
39 # Initialize a surface with width and height
40 new (w, h: Int) import enable_mouse_motion_events `{
41 SDL_Init(SDL_INIT_VIDEO);
42
43 if(TTF_Init()==-1) {
44 printf("TTF_Init: %s\n", TTF_GetError());
45 exit(2);
46 }
47
48 SDL_Surface *self = SDL_SetVideoMode(w, h, 24, SDL_HWSURFACE);
49
50 if (!SDLDisplay_enable_mouse_motion_events(self)) {
51 /* ignores mousemotion for performance reasons */
52 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
53 }
54
55 return self;
56 `}
57
58 # Indicates wether we want the SDL mouse motion event (or only clicks).
59 # Disabled by defaut for performance reason. To activate, redef this method
60 # andd return true
61 fun enable_mouse_motion_events: Bool do return false
62
63 # Destroy the surface
64 fun destroy `{
65 if (SDL_WasInit(SDL_INIT_VIDEO))
66 SDL_Quit();
67
68 if (TTF_WasInit())
69 TTF_Quit();
70 `}
71
72 redef fun finish `{ SDL_Flip(recv); `}
73
74 # Clear the entire window with given RGB color (integer values)
75 fun clear_int(r, g, b: Int) `{
76 SDL_FillRect(recv, NULL, SDL_MapRGB(recv->format,r,g,b));
77 `}
78
79 redef fun width: Int `{ return recv->w; `}
80 redef fun height: Int `{ return recv->h; `}
81
82 # Fill a rectangle with given color
83 fun fill_rect(rect: SDLRectangle, r, g, b: Int) `{
84 SDL_FillRect(recv, rect, SDL_MapRGB(recv->format,r,g,b));
85 `}
86
87 redef fun clear(r, g, b: Float) `{
88 Uint8 ri, gi, bi;
89 ri = (Uint8)r*255;
90 gi = (Uint8)g*255;
91 bi = (Uint8)b*255;
92 SDL_FillRect(recv, NULL, SDL_MapRGB(recv->format,ri,gi,bi));
93 `}
94
95 # SDL events since the last call to this method
96 fun events: Sequence[SDLInputEvent]
97 do
98 var events = new Array[SDLInputEvent]
99 loop
100 var new_event = poll_event
101 if new_event == null then break
102 events.add new_event
103 end
104 return events
105 end
106
107 private fun poll_event: nullable SDLInputEvent import SDLKeyEvent, SDLMouseButtonEvent, SDLMouseMotionEvent, SDLQuitEvent, NativeString.to_s, SDLMouseButtonEvent.as(nullable SDLInputEvent), SDLMouseMotionEvent.as(nullable SDLInputEvent), SDLKeyEvent.as(nullable SDLInputEvent), SDLQuitEvent.as(nullable SDLInputEvent) `{
108 SDL_Event event;
109
110 SDL_PumpEvents();
111
112 if (SDL_PollEvent(&event))
113 {
114 switch (event.type) {
115 case SDL_KEYDOWN:
116 case SDL_KEYUP:
117 #ifdef DEBUG
118 printf("The \"%s\" key was pressed!\n",
119 SDL_GetKeyName(event.key.keysym.sym));
120 #endif
121
122 return SDLKeyEvent_as_nullable_SDLInputEvent(
123 new_SDLKeyEvent(NativeString_to_s(
124 SDL_GetKeyName(event.key.keysym.sym)),
125 event.type==SDL_KEYDOWN));
126
127 case SDL_MOUSEMOTION:
128 #ifdef DEBUG
129 printf("Mouse moved by %d,%d to (%d,%d)\n",
130 event.motion.xrel, event.motion.yrel,
131 event.motion.x, event.motion.y);
132 #endif
133
134 return SDLMouseMotionEvent_as_nullable_SDLInputEvent(
135 new_SDLMouseMotionEvent(event.motion.x, event.motion.y,
136 event.motion.xrel, event.motion.yrel, SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)));
137
138 case SDL_MOUSEBUTTONDOWN:
139 case SDL_MOUSEBUTTONUP:
140 #ifdef DEBUG
141 printf("Mouse button \"%d\" pressed at (%d,%d)\n",
142 event.button.button, event.button.x, event.button.y);
143 #endif
144 return SDLMouseButtonEvent_as_nullable_SDLInputEvent(
145 new_SDLMouseButtonEvent(event.button.x, event.button.y,
146 event.button.button, event.type == SDL_MOUSEBUTTONDOWN));
147
148 case SDL_QUIT:
149 #ifdef DEBUG
150 printf("Quit event\n");
151 #endif
152 return SDLQuitEvent_as_nullable_SDLInputEvent(new_SDLQuitEvent());
153 }
154 }
155
156 return null_SDLInputEvent();
157 `}
158
159 # Set the position of the cursor to x,y
160 fun warp_mouse(x,y: Int) `{ SDL_WarpMouse(x, y); `}
161
162 # Show or hide the cursor
163 fun show_cursor=(val: Bool) `{ SDL_ShowCursor(val? SDL_ENABLE: SDL_DISABLE); `}
164
165 # Is the cursor visible?
166 fun show_cursor: Bool `{ SDL_ShowCursor(SDL_QUERY); `}
167
168 # Grab or release the input
169 fun grab_input=(val: Bool) `{ SDL_WM_GrabInput(val? SDL_GRAB_ON: SDL_GRAB_OFF); `}
170
171 # Is the input grabbed?
172 fun grab_input: Bool `{ SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; `}
173
174 # Are instances of `SDLMouseMotionEvent` ignored?
175 fun ignore_mouse_motion_events: Bool `{
176 return SDL_EventState(SDL_MOUSEMOTION, SDL_QUERY);
177 `}
178
179 # Do not raise instances of `SDLMouseMotionEvent` if `val`
180 fun ignore_mouse_motion_events=(val: Bool) `{
181 SDL_EventState(SDL_MOUSEMOTION, val? SDL_IGNORE: SDL_ENABLE);
182 `}
183 end
184
185 # Basic Drawing figures
186 extern class SDLDrawable `{SDL_Surface*`}
187 super Drawable
188
189 redef type I: SDLImage
190
191 redef fun blit(img, x, y) do native_blit(img, x.to_i, y.to_i)
192 private fun native_blit(img: I, x, y: Int) `{
193 SDL_Rect dst;
194 dst.x = x;
195 dst.y = y;
196 dst.w = 0;
197 dst.h = 0;
198
199 SDL_BlitSurface(img, NULL, recv, &dst);
200 `}
201
202 redef fun blit_centered(img, x, y)
203 do
204 x = x - img.width / 2
205 y = y - img.height / 2
206 blit(img, x, y)
207 end
208 end
209
210 # A drawable Image
211 extern class SDLImage
212 super DrawableImage
213 super SDLDrawable
214
215 # Import image from a file
216 new from_file(path: String) import String.to_cstring `{
217 SDL_Surface *image = IMG_Load(String_to_cstring(path));
218 return image;
219 `}
220
221 # Copy of an existing SDLImage
222 new copy_of(image: SDLImage) `{
223 SDL_Surface *new_image = SDL_CreateRGBSurface(
224 image->flags, image->w, image->h, 24,
225 0, 0, 0, 0);
226
227 SDL_Rect dst;
228 dst.x = 0;
229 dst.y = 0;
230 dst.w = image->w;
231 dst.h = image->h;
232 SDL_BlitSurface(image, NULL, new_image, &dst);
233
234 return new_image;
235 `}
236
237 # Save the image into the specified file
238 fun save_to_file(path: String) import String.to_cstring `{ `}
239
240 # Destroy the image and free the memory
241 redef fun destroy `{ SDL_FreeSurface(recv); `}
242
243 redef fun width: Int `{ return recv->w; `}
244 redef fun height: Int `{ return recv->h; `}
245
246 fun is_ok: Bool do return not address_is_null
247 end
248
249 # A simple rectangle
250 extern class SDLRectangle `{SDL_Rect*`}
251 # Constructor with x,y positions width and height of the rectangle
252 new (x: Int, y: Int, w: Int, h: Int) `{
253 SDL_Rect *rect = malloc(sizeof(SDL_Rect));
254 rect->x = (Sint16)x;
255 rect->y = (Sint16)y;
256 rect->w = (Uint16)w;
257 rect->h = (Uint16)h;
258 return rect;
259 `}
260
261 fun x=(v: Int) `{ recv->x = (Sint16)v; `}
262 fun x: Int `{ return recv->x; `}
263
264 fun y=(v: Int) `{ recv->y = (Sint16)v; `}
265 fun y: Int `{ return recv->y; `}
266
267 fun w=(v: Int) `{ recv->w = (Uint16)v; `}
268 fun w: Int `{ return recv->w; `}
269
270 fun h=(v: Int) `{ recv->h = (Uint16)v; `}
271 fun h: Int `{ return recv->h; `}
272 end
273
274 interface SDLInputEvent
275 super InputEvent
276 end
277
278 # MouseEvent class containing the cursor position
279 class SDLMouseEvent
280 super PointerEvent
281 super SDLInputEvent
282
283 redef var x: Float
284 redef var y: Float
285
286 private init (x, y: Float)
287 do
288 self.x = x
289 self.y = y
290 end
291 end
292
293 # MouseButtonEvent used to get information when a button is pressed/depressed
294 class SDLMouseButtonEvent
295 super SDLMouseEvent
296
297 var button: Int
298
299 redef var pressed: Bool
300 redef fun depressed: Bool do return not pressed
301
302 # Is this event raised by the left button?
303 fun is_left_button: Bool do return button == 1
304
305 # Is this event raised by the right button?
306 fun is_right_button: Bool do return button == 2
307
308 # Is this event raised by the middle button?
309 fun is_middle_button: Bool do return button == 3
310
311 # Is this event raised by the wheel going down?
312 fun is_down_wheel: Bool do return button == 4
313
314 # Is this event raised by the wheel going up?
315 fun is_up_wheel: Bool do return button == 5
316
317 # Is this event raised by the wheel?
318 fun is_wheel: Bool do return is_down_wheel or is_up_wheel
319
320 init (x, y: Float, button: Int, pressed: Bool)
321 do
322 super(x, y)
323
324 self.button = button
325 self.pressed = pressed
326 end
327
328 redef fun to_s
329 do
330 if pressed then
331 return "MouseButtonEvent button {button} down at {x}, {y}"
332 else
333 return "MouseButtonEvent button {button} up at {x}, {y}"
334 end
335 end
336 end
337
338 # MouseMotionEvent to get the cursor position when the mouse is moved
339 class SDLMouseMotionEvent
340 super SDLMouseEvent
341
342 var rel_x: Float
343 var rel_y: Float
344
345 redef var pressed: Bool
346 redef fun depressed: Bool do return not pressed
347
348 init (x, y, rel_x, rel_y: Float, pressed: Bool)
349 do
350 super(x, y)
351
352 self.rel_x = rel_x
353 self.rel_y = rel_y
354 self.pressed = pressed
355 end
356
357 redef fun to_s do return "MouseMotionEvent at {x}, {y}"
358 end
359
360 # SDLKeyEvent for when a key is pressed
361 class SDLKeyEvent
362 super KeyEvent
363 super SDLInputEvent
364
365 var key_name: String
366 var down: Bool
367
368 init (key_name: String, down: Bool)
369 do
370 self.key_name = key_name
371 self.down = down
372 end
373
374 redef fun to_c: nullable Char
375 do
376 if key_name.length == 1 then return key_name.chars.first
377 return null
378 end
379
380 redef fun to_s
381 do
382 if down then
383 return "KeyboardEvent key {key_name} down"
384 else
385 return "KeyboardEvent key {key_name} up"
386 end
387 end
388
389 # Return true if the key is down, false otherwise
390 redef fun is_down do return down
391
392 # Return true if the key is the up arrow
393 redef fun is_arrow_up do return key_name == "up"
394 # Return true if the key is the left arrow
395 redef fun is_arrow_left do return key_name == "left"
396 # Return true if the key is the down arrow
397 redef fun is_arrow_down do return key_name == "down"
398 # Return true if the key is the right arrow
399 redef fun is_arrow_right do return key_name == "right"
400 end
401
402 class SDLQuitEvent
403 super SDLInputEvent
404 super QuitEvent
405 end
406
407 redef class Int
408 fun delay `{ SDL_Delay(recv); `}
409 end
410
411 # Class to load and use TTF_Font
412 extern class SDLFont `{TTF_Font *`}
413 # Load a font with a specified name and size
414 new (name: String, points: Int) import String.to_cstring `{
415 char * cname = String_to_cstring(name);
416
417 TTF_Font *font = TTF_OpenFont(cname, (int)points);
418 if(!font) {
419 printf("TTF_OpenFont: %s\n", TTF_GetError());
420 exit(1);
421 }
422
423 return font;
424 `}
425
426 fun destroy `{ TTF_CloseFont(recv); `}
427
428 # Create a String with the specified color, return an SDLImage
429 fun render(text: String, r, g, b: Int): SDLImage import String.to_cstring `{
430 SDL_Color color;
431 SDL_Surface *text_surface;
432 char *ctext;
433
434 color.r = r;
435 color.g = g;
436 color.b = b;
437
438 ctext = String_to_cstring(text);
439 if(!(text_surface=TTF_RenderText_Blended(recv, ctext, color)))
440 {
441 fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
442 exit(1);
443 }
444 else
445 return text_surface;
446 `}
447
448 # TODO reactivate fun below when updating libsdl_ttf to 2.0.10 or above
449 #fun outline: Int # TODO check to make inline/nitside only
450 #fun outline=(v: Int) is extern
451
452 #fun kerning: Bool is extern
453 #fun kerning=(v: Bool) is extern
454
455 # Maximum pixel height of all glyphs of this font.
456 fun height: Int `{
457 return TTF_FontHeight(recv);
458 `}
459
460 fun ascent: Int `{
461 return TTF_FontAscent(recv);
462 `}
463
464 fun descent: Int `{
465 return TTF_FontDescent(recv);
466 `}
467
468 # Get the recommended pixel height of a rendered line of text of the loaded font. This is usually larger than the Font.height.
469 fun line_skip: Int `{
470 return TTF_FontLineSkip(recv);
471 `}
472
473 # Return true is the font used fixed width for each char
474 fun is_fixed_width: Bool `{
475 return TTF_FontFaceIsFixedWidth(recv);
476 `}
477
478 # Return the family name of the font
479 fun family_name: nullable String import String.to_cstring, String.as nullable `{
480 char *fn = TTF_FontFaceFamilyName(recv);
481
482 if (fn == NULL)
483 return null_String();
484 else
485 return String_as_nullable(NativeString_to_s(fn));
486 `}
487
488 # Return the style name of the font
489 fun style_name: nullable String import String.to_cstring, String.as nullable `{
490 char *sn = TTF_FontFaceStyleName(recv);
491
492 if (sn == NULL)
493 return null_String();
494 else
495 return String_as_nullable(NativeString_to_s(sn));
496 `}
497
498 # Return the estimated width of a String if used with the current font
499 fun width_of(text: String): Int import NativeString.to_s `{
500 char *ctext = String_to_cstring(text);
501 int w;
502 if (TTF_SizeText(recv, ctext, &w, NULL))
503 {
504 fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
505 exit(1);
506 }
507 else
508 return w;
509 `}
510 end
511
512 # Information on the SDL window
513 # Used in other modules to get window handle
514 extern class SDLSystemWindowManagerInfo `{SDL_SysWMinfo *`}
515
516 new `{
517 SDL_SysWMinfo *val = malloc(sizeof(SDL_SysWMinfo));
518
519 SDL_VERSION(&val->version);
520
521 if(SDL_GetWMInfo(val) <= 0) {
522 printf("Unable to get window handle");
523 return 0;
524 }
525
526 return val;
527 `}
528
529 # Returns the handle of this window on a X11 window system
530 fun x11_window_handle: Pointer `{
531 return (void*)recv->info.x11.window;
532 `}
533 end