X-Git-Url: http://nitlanguage.org diff --git a/lib/sdl.nit b/lib/sdl.nit index 14db3ba..6de6d2d 100644 --- a/lib/sdl.nit +++ b/lib/sdl.nit @@ -1,203 +1,546 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2006 Jean Privat +# Copyright 2011-2013 Alexis Laferrière # -# 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 +# 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 + cflags exec("sdl-config", "--cflags") + ldflags(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf") +end + +import mnit_display +import c + +in "C header" `{ + #include + #include + #include + #include + #include +`} + +# Represent a screen surface +extern class SDLDisplay `{SDL_Surface *`} + super Display + + redef type I: SDLImage + + # Initialize a surface with width and height + new (w, h: Int) import enable_mouse_motion_events `{ + SDL_Init(SDL_INIT_VIDEO); + + if(TTF_Init()==-1) { + printf("TTF_Init: %s\n", TTF_GetError()); + exit(2); + } + + SDL_Surface *self = SDL_SetVideoMode(w, h, 24, SDL_HWSURFACE); + + if (!SDLDisplay_enable_mouse_motion_events(self)) { + /* ignores mousemotion for performance reasons */ + SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); + } + + return self; + `} + + # 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 + + # Destroy the surface + fun destroy `{ + if (SDL_WasInit(SDL_INIT_VIDEO)) + SDL_Quit(); + + if (TTF_WasInit()) + TTF_Quit(); + `} + + redef fun finish `{ SDL_Flip(recv); `} + + # 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)); + `} + + 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)); + `} + + # SDL events since the last call to this method + fun events: Sequence[SDLInputEvent] do - update_rect(0, 0, 0, 0) + var events = new Array[SDLInputEvent] + loop + var new_event = poll_event + if new_event == null then break + events.add new_event + end + return events end - fun clear is extern "sdl_clear_sruface" - - fun to_display_format: SDL_Surface is extern "SDL_DisplayFormat" - fun free is extern "SDL_FreeSurface" + 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) `{ + 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_SDLInputEvent( + 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_SDLInputEvent( + 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_SDLInputEvent( + 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_SDLInputEvent(new_SDLQuitEvent()); + } + } + + return null_SDLInputEvent(); + `} + + # 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=(val: Bool) `{ SDL_ShowCursor(val? SDL_ENABLE: SDL_DISABLE); `} + + # Is the cursor visible? + fun show_cursor: Bool `{ SDL_ShowCursor(SDL_QUERY); `} + + # Grab or release the input + fun grab_input=(val: Bool) `{ SDL_WM_GrabInput(val? SDL_GRAB_ON: SDL_GRAB_OFF); `} + + # Is the input grabbed? + fun grab_input: Bool `{ SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; `} + + # Are instances of `SDLMouseMotionEvent` ignored? + fun ignore_mouse_motion_events: Bool `{ + return SDL_EventState(SDL_MOUSEMOTION, SDL_QUERY); + `} + + # Do not raise instances of `SDLMouseMotionEvent` if `val` + fun ignore_mouse_motion_events=(val: Bool) `{ + SDL_EventState(SDL_MOUSEMOTION, val? SDL_IGNORE: SDL_ENABLE); + `} + + # Does `self` has the mouse focus? + fun mouse_focus: Bool `{ return SDL_GetAppState() & SDL_APPMOUSEFOCUS; `} + + # Does `self` has the input focus? + fun input_focus: Bool `{ return SDL_GetAppState() & SDL_APPINPUTFOCUS; `} end -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 +# Basic Drawing figures +extern class SDLDrawable `{SDL_Surface*`} + super Drawable -extern SDL_ButtonEvent - super SDL_Event - fun is_pressed: Bool is abstract + redef type I: SDLImage + + redef fun blit(img, x, y) do native_blit(img, x.to_i, y.to_i) + private fun native_blit(img: I, x, y: Int) `{ + 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 + x = x - img.width / 2 + y = y - img.height / 2 + blit(img, x, y) + end end -extern SDL_MouseEvent - super SDL_Event - fun x: Int is abstract - fun y: Int is abstract +# 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 + + # Returns a reference to the pixels of the texture + fun pixels: NativeCByteArray `{ return recv->pixels; `} + + # Does this texture has an alpha mask? + fun amask: Bool `{ return recv->format->Amask; `} end -extern SDL_KeyboardEvent - super SDL_ButtonEvent - redef fun is_pressed: Bool is extern "sdl_keyboard_evt_state" +# 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 -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" +interface SDLInputEvent + super InputEvent end -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" +# 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 + self.x = x + self.y = y + end end -class SDL_EventListener - fun on_keyboard(evt: SDL_KeyboardEvent) - do end - - fun on_mouse_button(evt: SDL_MouseButtonEvent) - do end +# MouseButtonEvent used to get information when a button is pressed/depressed +class SDLMouseButtonEvent + super SDLMouseEvent - fun on_mouse_motion(evt: SDL_MouseMotionEvent) - do end + var button: Int - fun on_expose - do end + redef var pressed: Bool + redef fun depressed: Bool do return not pressed - fun on_quit - do end -end + # Is this event raised by the left button? + fun is_left_button: Bool do return button == 1 -class SDL_EventProcessor - var _listeners: Array[SDL_EventListener] + # Is this event raised by the right button? + fun is_right_button: Bool do return button == 3 - fun add_listener(l: SDL_EventListener) - do - _listeners.add(l) - end + # Is this event raised by the middle button? + fun is_middle_button: Bool do return button == 2 - fun remove_listener(l: SDL_EventListener) - do - _listeners.remove(l) - end - - fun process_one_event - do - if sdl_poll_next_event then - process_event(sdl_current_event) - end - end + # Is this event raised by the wheel going down? + fun is_down_wheel: Bool do return button == 4 + + # Is this event raised by the wheel going up? + fun is_up_wheel: Bool do return button == 5 + + # Is this event raised by the wheel? + fun is_wheel: Bool do return is_down_wheel or is_up_wheel - fun process_all_events + 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 + + redef var pressed: Bool + redef fun depressed: Bool do return not pressed - init + 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 -fun sdl_init is extern + redef var name + var down: Bool -# Video + init (key_name: String, down: Bool) + do + self.name = key_name + self.down = down + end + + redef fun to_c: nullable Char + do + if name.length == 1 then return 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 {name} down" + else + return "KeyboardEvent 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 name == "up" + # Return true if the key is the left arrow + redef fun is_arrow_left do return name == "left" + # Return true if the key is the down arrow + redef fun is_arrow_down do return name == "down" + # Return true if the key is the right arrow + redef fun is_arrow_right do return 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