# This file is part of NIT ( http://www.nitlanguage.org ).
#
-# Copyright 2006 Jean Privat <jean@pryen.org>
+# Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Binding to the SDL multomedia library
-package sdl
-
-extern SDL_Surface
- super Pointer
- fun width: Int is extern "sdl_surface_width"
- fun height: Int is extern "sdl_surface_height"
-
- fun lock_surface: Int is extern "SDL_LockSurface"
- fun unlock_surface: Int is extern "SDL_LockSurface"
-
- fun blit_on(dest: SDL_Surface) is extern "sdl_blit_surface"
- fun blit_on_xy(dest: SDL_Surface, dest_x: Int, dest_y: Int) is extern "sdl_blit_surface_xy"
-
- fun update_rect(x: Int, y: Int, w: Int, h: Int) is extern "SDL_UpdateRect"
- fun update_all
- do
- update_rect(0, 0, 0, 0)
- end
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# SDL display support (used in Linux for windows and inputes only)
+module sdl is
+ c_compiler_option(exec("sdl-config", "--cflags"))
+ c_linker_option(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf")
+end
- fun clear is extern "sdl_clear_sruface"
+import mnit_display
- fun to_display_format: SDL_Surface is extern "SDL_DisplayFormat"
- fun free is extern "SDL_FreeSurface"
-end
+in "C header" `{
+ #include <unistd.h>
+ #include <SDL/SDL.h>
+ #include <SDL/SDL_syswm.h>
+ #include <SDL/SDL_image.h>
+ #include <SDL/SDL_ttf.h>
+`}
-extern SDL_Screen
- super SDL_Surface
- fun flip is extern "SDL_Flip"
-end
-
-extern SDL_Event
- fun is_keyboard: Bool is extern "sdl_evt_is_keyboard"
- fun as_keyboard: SDL_KeyboardEvent is extern "sdl_evt_as_keyboard"
- fun is_mouse_button: Bool is extern "sdl_evt_is_mouse_button"
- fun as_mouse_button: SDL_MouseButtonEvent is extern "sdl_evt_as_mouse_button"
- fun is_mouse_motion: Bool is extern "sdl_evt_is_mouse_motion"
- fun as_mouse_motion: SDL_MouseMotionEvent is extern "sdl_evt_as_mouse_motion"
- fun is_expose: Bool is extern "sdl_evt_is_expose"
- fun is_quit: Bool is extern "sdl_evt_is_quit"
-end
+# Represent a screen surface
+extern class SDLDisplay `{SDL_Surface *`}
+ super Display
-extern SDL_ButtonEvent
- super SDL_Event
- fun is_pressed: Bool is abstract
-end
+ redef type I: SDLImage
-extern SDL_MouseEvent
- super SDL_Event
- fun x: Int is abstract
- fun y: Int is abstract
-end
+ # Initialize a surface with width and height
+ new (w, h: Int) import enable_mouse_motion_events `{
+ SDL_Init(SDL_INIT_VIDEO);
-extern SDL_KeyboardEvent
- super SDL_ButtonEvent
- redef fun is_pressed: Bool is extern "sdl_keyboard_evt_state"
-end
+ if(TTF_Init()==-1) {
+ printf("TTF_Init: %s\n", TTF_GetError());
+ exit(2);
+ }
-extern SDL_MouseButtonEvent
- super SDL_ButtonEvent
- super SDL_MouseEvent
- redef fun is_pressed: Bool is extern "sdl_mouse_button_evt_state"
- redef fun x: Int is extern "sdl_mouse_button_evt_x"
- redef fun y: Int is extern "sdl_mouse_button_evt_y"
- fun button: Int is extern "sdl_mouse_button_evt_button"
-end
+ SDL_Surface *self = SDL_SetVideoMode(w, h, 24, SDL_HWSURFACE);
-extern SDL_MouseMotionEvent
- super SDL_MouseEvent
- redef fun x: Int is extern "sdl_mouse_evt_x"
- redef fun y: Int is extern "sdl_mouse_evt_y"
- fun xrel: Int is extern "sdl_mouse_evt_xrel"
- fun yrel: Int is extern "sdl_mouse_evt_yrel"
-end
+ if (!SDLDisplay_enable_mouse_motion_events(self)) {
+ /* ignores mousemotion for performance reasons */
+ SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+ }
-class SDL_EventListener
- fun on_keyboard(evt: SDL_KeyboardEvent)
- do end
-
- fun on_mouse_button(evt: SDL_MouseButtonEvent)
- do end
+ return self;
+ `}
- fun on_mouse_motion(evt: SDL_MouseMotionEvent)
- do end
+ # Indicates wether we want the SDL mouse motion event (or only clicks).
+ # Disabled by defaut for performance reason. To activate, redef this method
+ # andd return true
+ fun enable_mouse_motion_events: Bool do return false
- fun on_expose
- do end
+ # Destroy the surface
+ fun destroy `{
+ if (SDL_WasInit(SDL_INIT_VIDEO))
+ SDL_Quit();
- fun on_quit
- do end
-end
+ if (TTF_WasInit())
+ TTF_Quit();
+ `}
+
+ redef fun finish `{ SDL_Flip(recv); `}
-class SDL_EventProcessor
- var _listeners: Array[SDL_EventListener]
+ # Clear the entire window with given RGB color (integer values)
+ fun clear_int(r, g, b: Int) `{
+ SDL_FillRect(recv, NULL, SDL_MapRGB(recv->format,r,g,b));
+ `}
- fun add_listener(l: SDL_EventListener)
+ redef fun width: Int `{ return recv->w; `}
+ redef fun height: Int `{ return recv->h; `}
+
+ # Fill a rectangle with given color
+ fun fill_rect(rect: SDLRectangle, r, g, b: Int) `{
+ SDL_FillRect(recv, rect, SDL_MapRGB(recv->format,r,g,b));
+ `}
+
+ redef fun clear(r, g, b: Float) `{
+ Uint8 ri, gi, bi;
+ ri = (Uint8)r*255;
+ gi = (Uint8)g*255;
+ bi = (Uint8)b*255;
+ SDL_FillRect(recv, NULL, SDL_MapRGB(recv->format,ri,gi,bi));
+ `}
+
+ fun events: Sequence[IE]
do
- _listeners.add(l)
+ var new_event: nullable Object = null
+ var events = new List[IE]
+ loop
+ new_event = poll_event
+ if new_event != null then # new_event isa Event then #
+ events.add(new_event)
+ else
+ break
+ end
+ end
+ return events
end
- fun remove_listener(l: SDL_EventListener)
+ private fun poll_event: nullable IE import SDLKeyEvent, SDLMouseButtonEvent, SDLMouseMotionEvent, SDLQuitEvent, NativeString.to_s, SDLMouseButtonEvent.as(nullable IE), SDLMouseMotionEvent.as(nullable IE), SDLKeyEvent.as(nullable IE), SDLQuitEvent.as(nullable IE) `{
+ SDL_Event event;
+
+ SDL_PumpEvents();
+
+ if (SDL_PollEvent(&event))
+ {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ #ifdef DEBUG
+ printf("The \"%s\" key was pressed!\n",
+ SDL_GetKeyName(event.key.keysym.sym));
+ #endif
+
+ return SDLKeyEvent_as_nullable_IE(
+ new_SDLKeyEvent(NativeString_to_s(
+ SDL_GetKeyName(event.key.keysym.sym)),
+ event.type==SDL_KEYDOWN));
+
+ case SDL_MOUSEMOTION:
+ #ifdef DEBUG
+ printf("Mouse moved by %d,%d to (%d,%d)\n",
+ event.motion.xrel, event.motion.yrel,
+ event.motion.x, event.motion.y);
+ #endif
+
+ return SDLMouseMotionEvent_as_nullable_IE(
+ new_SDLMouseMotionEvent(event.motion.x, event.motion.y,
+ event.motion.xrel, event.motion.yrel, SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)));
+
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ #ifdef DEBUG
+ printf("Mouse button \"%d\" pressed at (%d,%d)\n",
+ event.button.button, event.button.x, event.button.y);
+ #endif
+ return SDLMouseButtonEvent_as_nullable_IE(
+ new_SDLMouseButtonEvent(event.button.x, event.button.y,
+ event.button.button, event.type == SDL_MOUSEBUTTONDOWN));
+
+ case SDL_QUIT:
+ #ifdef DEBUG
+ printf("Quit event\n");
+ #endif
+ return SDLQuitEvent_as_nullable_IE(new_SDLQuitEvent());
+ }
+ }
+
+ return null_InputEvent();
+ `}
+
+ # Set the position of the cursor to x,y
+ fun warp_mouse(x,y: Int) `{ SDL_WarpMouse(x, y); `}
+
+ # Show or hide the cursor
+ fun show_cursor(show: Bool) `{ SDL_ShowCursor(show); `}
+end
+
+# Basic Drawing figures
+extern class SDLDrawable `{SDL_Surface*`}
+ super Drawable
+
+ redef type I: SDLImage
+
+ redef fun blit(img, x, y) `{
+ SDL_Rect dst;
+ dst.x = x;
+ dst.y = y;
+ dst.w = 0;
+ dst.h = 0;
+
+ SDL_BlitSurface(img, NULL, recv, &dst);
+ `}
+
+ redef fun blit_centered(img, x, y)
do
- _listeners.remove(l)
+ x = x - img.width / 2
+ y = y - img.height / 2
+ blit(img, x, y)
end
-
- fun process_one_event
+end
+
+# A drawable Image
+extern class SDLImage
+ super DrawableImage
+ super SDLDrawable
+
+ # Import image from a file
+ new from_file(path: String) import String.to_cstring `{
+ SDL_Surface *image = IMG_Load(String_to_cstring(path));
+ return image;
+ `}
+
+ # Copy of an existing SDLImage
+ new copy_of(image: SDLImage) `{
+ SDL_Surface *new_image = SDL_CreateRGBSurface(
+ image->flags, image->w, image->h, 24,
+ 0, 0, 0, 0);
+
+ SDL_Rect dst;
+ dst.x = 0;
+ dst.y = 0;
+ dst.w = image->w;
+ dst.h = image->h;
+ SDL_BlitSurface(image, NULL, new_image, &dst);
+
+ return new_image;
+ `}
+
+ # Save the image into the specified file
+ fun save_to_file(path: String) import String.to_cstring `{ `}
+
+ # Destroy the image and free the memory
+ redef fun destroy `{ SDL_FreeSurface(recv); `}
+
+ redef fun width: Int `{ return recv->w; `}
+ redef fun height: Int `{ return recv->h; `}
+
+ fun is_ok: Bool do return not address_is_null
+end
+
+# A simple rectangle
+extern class SDLRectangle `{SDL_Rect*`}
+ # Constructor with x,y positions width and height of the rectangle
+ new (x: Int, y: Int, w: Int, h: Int) `{
+ SDL_Rect *rect = malloc(sizeof(SDL_Rect));
+ rect->x = (Sint16)x;
+ rect->y = (Sint16)y;
+ rect->w = (Uint16)w;
+ rect->h = (Uint16)h;
+ return rect;
+ `}
+
+ fun x=(v: Int) `{ recv->x = (Sint16)v; `}
+ fun x: Int `{ return recv->x; `}
+
+ fun y=(v: Int) `{ recv->y = (Sint16)v; `}
+ fun y: Int `{ return recv->y; `}
+
+ fun w=(v: Int) `{ recv->w = (Uint16)v; `}
+ fun w: Int `{ return recv->w; `}
+
+ fun h=(v: Int) `{ recv->h = (Uint16)v; `}
+ fun h: Int `{ return recv->h; `}
+end
+
+interface SDLInputEvent
+ super InputEvent
+end
+
+# MouseEvent class containing the cursor position
+class SDLMouseEvent
+ super PointerEvent
+ super SDLInputEvent
+
+ redef var x: Float
+ redef var y: Float
+
+ private init (x, y: Float)
do
- if sdl_poll_next_event then
- process_event(sdl_current_event)
- end
+ self.x = x
+ self.y = y
end
+end
+
+# MouseButtonEvent used to get information when a button is pressed/depressed
+class SDLMouseButtonEvent
+ super SDLMouseEvent
+
+ var button: Int
- fun process_all_events
+ redef var pressed: Bool
+ redef fun depressed: Bool do return not pressed
+
+ init (x, y: Float, button: Int, pressed: Bool)
do
- while sdl_poll_next_event do
- process_event(sdl_current_event)
- end
+ super(x, y)
+
+ self.button = button
+ self.pressed = pressed
end
-
- private fun process_event(evt: SDL_Event)
+
+ redef fun to_s
do
- var sdl_listeners = _listeners
- if evt.is_keyboard then
- for i in sdl_listeners do
- i.on_keyboard(evt.as_keyboard)
- end
- else if evt.is_mouse_button then
- for i in sdl_listeners do
- i.on_mouse_button(evt.as_mouse_button)
- end
- else if evt.is_mouse_motion then
- for i in sdl_listeners do
- i.on_mouse_motion(evt.as_mouse_motion)
- end
- else if evt.is_expose then
- for i in sdl_listeners do
- i.on_expose
- end
- else if evt.is_quit then
- for i in sdl_listeners do
- i.on_quit
- end
+ if pressed then
+ return "MouseButtonEvent button {button} down at {x}, {y}"
+ else
+ return "MouseButtonEvent button {button} up at {x}, {y}"
end
end
+end
+
+# MouseMotionEvent to get the cursor position when the mouse is moved
+class SDLMouseMotionEvent
+ super SDLMouseEvent
+
+ var rel_x: Float
+ var rel_y: Float
- init
+ redef var pressed: Bool
+ redef fun depressed: Bool do return not pressed
+
+ init (x, y, rel_x, rel_y: Float, pressed: Bool)
do
- _listeners = new Array[SDL_EventListener]
+ super(x, y)
+
+ self.rel_x = rel_x
+ self.rel_y = rel_y
+ self.pressed = pressed
end
+
+ redef fun to_s do return "MouseMotionEvent at {x}, {y}"
end
-# General
+# SDLKeyEvent for when a key is pressed
+class SDLKeyEvent
+ super KeyEvent
+ super SDLInputEvent
+
+ var key_name: String
+ var down: Bool
-fun sdl_init is extern
+ init (key_name: String, down: Bool)
+ do
+ self.key_name = key_name
+ self.down = down
+ end
-# Video
+ redef fun to_c: nullable Char
+ do
+ if key_name.length == 1 then return key_name.chars.first
+ return null
+ end
-fun sdl_set_video_mode(w: Int, h: Int, d: Int): SDL_Screen is extern
-fun sdl_set_fullscreen_video_mode(w: Int, h: Int, d: Int): SDL_Screen is extern
+ redef fun to_s
+ do
+ if down then
+ return "KeyboardEvent key {key_name} down"
+ else
+ return "KeyboardEvent key {key_name} up"
+ end
+ end
-fun sdl_load_raw_bmp(s: String): SDL_Surface
-do
- return sdl_load_bmp_native(s.to_cstring)
+ # Return true if the key is down, false otherwise
+ redef fun is_down do return down
+
+ # Return true if the key is the up arrow
+ redef fun is_arrow_up do return key_name == "up"
+ # Return true if the key is the left arrow
+ redef fun is_arrow_left do return key_name == "left"
+ # Return true if the key is the down arrow
+ redef fun is_arrow_down do return key_name == "down"
+ # Return true if the key is the right arrow
+ redef fun is_arrow_right do return key_name == "right"
end
-fun sdl_load_bmp(s: String): SDL_Surface
-do
- var raw = sdl_load_raw_bmp(s)
- var sprite = raw.to_display_format
- raw.free
- return sprite
+
+class SDLQuitEvent
+ super SDLInputEvent
+ super QuitEvent
end
-fun sdl_load_bmp_native(s: NativeString): SDL_Surface is extern
+redef class Int
+ fun delay `{ SDL_Delay(recv); `}
+end
-fun sdl_show_cursor=(b: Bool) is extern "sdl_show_cursor_1"
-fun sdl_show_cursor: Bool is extern "sdl_show_cursor_0"
+# Class to load and use TTF_Font
+extern class SDLFont `{TTF_Font *`}
+ # Load a font with a specified name and size
+ new (name: String, points: Int) import String.to_cstring `{
+ char * cname = String_to_cstring(name);
+
+ TTF_Font *font = TTF_OpenFont(cname, (int)points);
+ if(!font) {
+ printf("TTF_OpenFont: %s\n", TTF_GetError());
+ exit(1);
+ }
+
+ return font;
+ `}
+
+ fun destroy `{ TTF_CloseFont(recv); `}
+
+ # Create a String with the specified color, return an SDLImage
+ fun render(text: String, r, g, b: Int): SDLImage import String.to_cstring `{
+ SDL_Color color;
+ SDL_Surface *text_surface;
+ char *ctext;
+
+ color.r = r;
+ color.g = g;
+ color.b = b;
+
+ ctext = String_to_cstring(text);
+ if(!(text_surface=TTF_RenderText_Blended(recv, ctext, color)))
+ {
+ fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
+ exit(1);
+ }
+ else
+ return text_surface;
+ `}
+
+ # TODO reactivate fun below when updating libsdl_ttf to 2.0.10 or above
+ #fun outline: Int # TODO check to make inline/nitside only
+ #fun outline=(v: Int) is extern
+
+ #fun kerning: Bool is extern
+ #fun kerning=(v: Bool) is extern
+
+ # Maximum pixel height of all glyphs of this font.
+ fun height: Int `{
+ return TTF_FontHeight(recv);
+ `}
+
+ fun ascent: Int `{
+ return TTF_FontAscent(recv);
+ `}
+
+ fun descent: Int `{
+ return TTF_FontDescent(recv);
+ `}
+
+ # Get the recommended pixel height of a rendered line of text of the loaded font. This is usually larger than the Font.height.
+ fun line_skip: Int `{
+ return TTF_FontLineSkip(recv);
+ `}
+
+ # Return true is the font used fixed width for each char
+ fun is_fixed_width: Bool `{
+ return TTF_FontFaceIsFixedWidth(recv);
+ `}
+
+ # Return the family name of the font
+ fun family_name: nullable String import String.to_cstring, String.as nullable `{
+ char *fn = TTF_FontFaceFamilyName(recv);
+
+ if (fn == NULL)
+ return null_String();
+ else
+ return String_as_nullable(NativeString_to_s(fn));
+ `}
+
+ # Return the style name of the font
+ fun style_name: nullable String import String.to_cstring, String.as nullable `{
+ char *sn = TTF_FontFaceStyleName(recv);
+
+ if (sn == NULL)
+ return null_String();
+ else
+ return String_as_nullable(NativeString_to_s(sn));
+ `}
+
+ # Return the estimated width of a String if used with the current font
+ fun width_of(text: String): Int import NativeString.to_s `{
+ char *ctext = String_to_cstring(text);
+ int w;
+ if (TTF_SizeText(recv, ctext, &w, NULL))
+ {
+ fprintf(stderr, "SDL TFF error: %s\n", TTF_GetError());
+ exit(1);
+ }
+ else
+ return w;
+ `}
+end
-# WM
+# Information on the SDL window
+# Used in other modules to get window handle
+extern class SDLSystemWindowManagerInfo `{SDL_SysWMinfo *`}
-fun sdl_grab=(b: Bool) is extern "sdl_grab_1"
-fun sdl_grab: Bool is extern "sdl_grab_0"
+ new `{
+ SDL_SysWMinfo *val = malloc(sizeof(SDL_SysWMinfo));
-# Events
+ SDL_VERSION(&val->version);
-fun sdl_current_event: SDL_Event is extern
-fun sdl_poll_next_event: Bool is extern
+ if(SDL_GetWMInfo(val) <= 0) {
+ printf("Unable to get window handle");
+ return 0;
+ }
-# Time
+ return val;
+ `}
-fun sdl_get_ticks: Int is extern
-fun sdl_delay(ms: Int) is extern
+ # Returns the handle of this window on a X11 window system
+ fun x11_window_handle: Pointer `{
+ return (void*)recv->info.x11.window;
+ `}
+end