mnit: remove virtual type IE
[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 fun events: Sequence[SDLInputEvent]
96 do
97 var new_event: nullable Object = null
98 var events = new List[SDLInputEvent]
99 loop
100 new_event = poll_event
101 if new_event != null then # new_event isa Event then #
102 events.add(new_event)
103 else
104 break
105 end
106 end
107 return events
108 end
109
110 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) `{
111 SDL_Event event;
112
113 SDL_PumpEvents();
114
115 if (SDL_PollEvent(&event))
116 {
117 switch (event.type) {
118 case SDL_KEYDOWN:
119 case SDL_KEYUP:
120 #ifdef DEBUG
121 printf("The \"%s\" key was pressed!\n",
122 SDL_GetKeyName(event.key.keysym.sym));
123 #endif
124
125 return SDLKeyEvent_as_nullable_SDLInputEvent(
126 new_SDLKeyEvent(NativeString_to_s(
127 SDL_GetKeyName(event.key.keysym.sym)),
128 event.type==SDL_KEYDOWN));
129
130 case SDL_MOUSEMOTION:
131 #ifdef DEBUG
132 printf("Mouse moved by %d,%d to (%d,%d)\n",
133 event.motion.xrel, event.motion.yrel,
134 event.motion.x, event.motion.y);
135 #endif
136
137 return SDLMouseMotionEvent_as_nullable_SDLInputEvent(
138 new_SDLMouseMotionEvent(event.motion.x, event.motion.y,
139 event.motion.xrel, event.motion.yrel, SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)));
140
141 case SDL_MOUSEBUTTONDOWN:
142 case SDL_MOUSEBUTTONUP:
143 #ifdef DEBUG
144 printf("Mouse button \"%d\" pressed at (%d,%d)\n",
145 event.button.button, event.button.x, event.button.y);
146 #endif
147 return SDLMouseButtonEvent_as_nullable_SDLInputEvent(
148 new_SDLMouseButtonEvent(event.button.x, event.button.y,
149 event.button.button, event.type == SDL_MOUSEBUTTONDOWN));
150
151 case SDL_QUIT:
152 #ifdef DEBUG
153 printf("Quit event\n");
154 #endif
155 return SDLQuitEvent_as_nullable_SDLInputEvent(new_SDLQuitEvent());
156 }
157 }
158
159 return null_SDLInputEvent();
160 `}
161
162 # Set the position of the cursor to x,y
163 fun warp_mouse(x,y: Int) `{ SDL_WarpMouse(x, y); `}
164
165 # Show or hide the cursor
166 fun show_cursor(show: Bool) `{ SDL_ShowCursor(show); `}
167 end
168
169 # Basic Drawing figures
170 extern class SDLDrawable `{SDL_Surface*`}
171 super Drawable
172
173 redef type I: SDLImage
174
175 redef fun blit(img, x, y) `{
176 SDL_Rect dst;
177 dst.x = x;
178 dst.y = y;
179 dst.w = 0;
180 dst.h = 0;
181
182 SDL_BlitSurface(img, NULL, recv, &dst);
183 `}
184
185 redef fun blit_centered(img, x, y)
186 do
187 x = x - img.width / 2
188 y = y - img.height / 2
189 blit(img, x, y)
190 end
191 end
192
193 # A drawable Image
194 extern class SDLImage
195 super DrawableImage
196 super SDLDrawable
197
198 # Import image from a file
199 new from_file(path: String) import String.to_cstring `{
200 SDL_Surface *image = IMG_Load(String_to_cstring(path));
201 return image;
202 `}
203
204 # Copy of an existing SDLImage
205 new copy_of(image: SDLImage) `{
206 SDL_Surface *new_image = SDL_CreateRGBSurface(
207 image->flags, image->w, image->h, 24,
208 0, 0, 0, 0);
209
210 SDL_Rect dst;
211 dst.x = 0;
212 dst.y = 0;
213 dst.w = image->w;
214 dst.h = image->h;
215 SDL_BlitSurface(image, NULL, new_image, &dst);
216
217 return new_image;
218 `}
219
220 # Save the image into the specified file
221 fun save_to_file(path: String) import String.to_cstring `{ `}
222
223 # Destroy the image and free the memory
224 redef fun destroy `{ SDL_FreeSurface(recv); `}
225
226 redef fun width: Int `{ return recv->w; `}
227 redef fun height: Int `{ return recv->h; `}
228
229 fun is_ok: Bool do return not address_is_null
230 end
231
232 # A simple rectangle
233 extern class SDLRectangle `{SDL_Rect*`}
234 # Constructor with x,y positions width and height of the rectangle
235 new (x: Int, y: Int, w: Int, h: Int) `{
236 SDL_Rect *rect = malloc(sizeof(SDL_Rect));
237 rect->x = (Sint16)x;
238 rect->y = (Sint16)y;
239 rect->w = (Uint16)w;
240 rect->h = (Uint16)h;
241 return rect;
242 `}
243
244 fun x=(v: Int) `{ recv->x = (Sint16)v; `}
245 fun x: Int `{ return recv->x; `}
246
247 fun y=(v: Int) `{ recv->y = (Sint16)v; `}
248 fun y: Int `{ return recv->y; `}
249
250 fun w=(v: Int) `{ recv->w = (Uint16)v; `}
251 fun w: Int `{ return recv->w; `}
252
253 fun h=(v: Int) `{ recv->h = (Uint16)v; `}
254 fun h: Int `{ return recv->h; `}
255 end
256
257 interface SDLInputEvent
258 super InputEvent
259 end
260
261 # MouseEvent class containing the cursor position
262 class SDLMouseEvent
263 super PointerEvent
264 super SDLInputEvent
265
266 redef var x: Float
267 redef var y: Float
268
269 private init (x, y: Float)
270 do
271 self.x = x
272 self.y = y
273 end
274 end
275
276 # MouseButtonEvent used to get information when a button is pressed/depressed
277 class SDLMouseButtonEvent
278 super SDLMouseEvent
279
280 var button: Int
281
282 redef var pressed: Bool
283 redef fun depressed: Bool do return not pressed
284
285 init (x, y: Float, button: Int, pressed: Bool)
286 do
287 super(x, y)
288
289 self.button = button
290 self.pressed = pressed
291 end
292
293 redef fun to_s
294 do
295 if pressed then
296 return "MouseButtonEvent button {button} down at {x}, {y}"
297 else
298 return "MouseButtonEvent button {button} up at {x}, {y}"
299 end
300 end
301 end
302
303 # MouseMotionEvent to get the cursor position when the mouse is moved
304 class SDLMouseMotionEvent
305 super SDLMouseEvent
306
307 var rel_x: Float
308 var rel_y: Float
309
310 redef var pressed: Bool
311 redef fun depressed: Bool do return not pressed
312
313 init (x, y, rel_x, rel_y: Float, pressed: Bool)
314 do
315 super(x, y)
316
317 self.rel_x = rel_x
318 self.rel_y = rel_y
319 self.pressed = pressed
320 end
321
322 redef fun to_s do return "MouseMotionEvent at {x}, {y}"
323 end
324
325 # SDLKeyEvent for when a key is pressed
326 class SDLKeyEvent
327 super KeyEvent
328 super SDLInputEvent
329
330 var key_name: String
331 var down: Bool
332
333 init (key_name: String, down: Bool)
334 do
335 self.key_name = key_name
336 self.down = down
337 end
338
339 redef fun to_c: nullable Char
340 do
341 if key_name.length == 1 then return key_name.chars.first
342 return null
343 end
344
345 redef fun to_s
346 do
347 if down then
348 return "KeyboardEvent key {key_name} down"
349 else
350 return "KeyboardEvent key {key_name} up"
351 end
352 end
353
354 # Return true if the key is down, false otherwise
355 redef fun is_down do return down
356
357 # Return true if the key is the up arrow
358 redef fun is_arrow_up do return key_name == "up"
359 # Return true if the key is the left arrow
360 redef fun is_arrow_left do return key_name == "left"
361 # Return true if the key is the down arrow
362 redef fun is_arrow_down do return key_name == "down"
363 # Return true if the key is the right arrow
364 redef fun is_arrow_right do return key_name == "right"
365 end
366
367 class SDLQuitEvent
368 super SDLInputEvent
369 super QuitEvent
370 end
371
372 redef class Int
373 fun delay `{ SDL_Delay(recv); `}
374 end
375
376 # Class to load and use TTF_Font
377 extern class SDLFont `{TTF_Font *`}
378 # Load a font with a specified name and size
379 new (name: String, points: Int) import String.to_cstring `{
380 char * cname = String_to_cstring(name);
381
382 TTF_Font *font = TTF_OpenFont(cname, (int)points);
383 if(!font) {
384 printf("TTF_OpenFont: %s\n", TTF_GetError());
385 exit(1);
386 }
387
388 return font;
389 `}
390
391 fun destroy `{ TTF_CloseFont(recv); `}
392
393 # Create a String with the specified color, return an SDLImage
394 fun render(text: String, r, g, b: Int): SDLImage import String.to_cstring `{
395 SDL_Color color;
396 SDL_Surface *text_surface;
397 char *ctext;
398
399 color.r = r;
400 color.g = g;
401 color.b = b;
402
403 ctext = String_to_cstring(text);
404 if(!(text_surface=TTF_RenderText_Blended(recv, ctext, color)))
405 {
406 fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
407 exit(1);
408 }
409 else
410 return text_surface;
411 `}
412
413 # TODO reactivate fun below when updating libsdl_ttf to 2.0.10 or above
414 #fun outline: Int # TODO check to make inline/nitside only
415 #fun outline=(v: Int) is extern
416
417 #fun kerning: Bool is extern
418 #fun kerning=(v: Bool) is extern
419
420 # Maximum pixel height of all glyphs of this font.
421 fun height: Int `{
422 return TTF_FontHeight(recv);
423 `}
424
425 fun ascent: Int `{
426 return TTF_FontAscent(recv);
427 `}
428
429 fun descent: Int `{
430 return TTF_FontDescent(recv);
431 `}
432
433 # Get the recommended pixel height of a rendered line of text of the loaded font. This is usually larger than the Font.height.
434 fun line_skip: Int `{
435 return TTF_FontLineSkip(recv);
436 `}
437
438 # Return true is the font used fixed width for each char
439 fun is_fixed_width: Bool `{
440 return TTF_FontFaceIsFixedWidth(recv);
441 `}
442
443 # Return the family name of the font
444 fun family_name: nullable String import String.to_cstring, String.as nullable `{
445 char *fn = TTF_FontFaceFamilyName(recv);
446
447 if (fn == NULL)
448 return null_String();
449 else
450 return String_as_nullable(NativeString_to_s(fn));
451 `}
452
453 # Return the style name of the font
454 fun style_name: nullable String import String.to_cstring, String.as nullable `{
455 char *sn = TTF_FontFaceStyleName(recv);
456
457 if (sn == NULL)
458 return null_String();
459 else
460 return String_as_nullable(NativeString_to_s(sn));
461 `}
462
463 # Return the estimated width of a String if used with the current font
464 fun width_of(text: String): Int import NativeString.to_s `{
465 char *ctext = String_to_cstring(text);
466 int w;
467 if (TTF_SizeText(recv, ctext, &w, NULL))
468 {
469 fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
470 exit(1);
471 }
472 else
473 return w;
474 `}
475 end
476
477 # Information on the SDL window
478 # Used in other modules to get window handle
479 extern class SDLSystemWindowManagerInfo `{SDL_SysWMinfo *`}
480
481 new `{
482 SDL_SysWMinfo *val = malloc(sizeof(SDL_SysWMinfo));
483
484 SDL_VERSION(&val->version);
485
486 if(SDL_GetWMInfo(val) <= 0) {
487 printf("Unable to get window handle");
488 return 0;
489 }
490
491 return val;
492 `}
493
494 # Returns the handle of this window on a X11 window system
495 fun x11_window_handle: Pointer `{
496 return (void*)recv->info.x11.window;
497 `}
498 end