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