# Gamnit display implementation for GNU/Linux using `egl`, `sdl` and `x11`
module display_linux
-import sdl
-import x11
+import sdl2::image
import egl # local to gamnit
intrude import display
fun height=(value: Int) do requested_height = value
private var requested_height = 1080
- redef fun show_cursor do return sdl_display.show_cursor
+ redef fun show_cursor do return sdl.show_cursor
- redef fun show_cursor=(val) do sdl_display.show_cursor = val
+ redef fun show_cursor=(val) do sdl.show_cursor = val
- # Setup SDL, X11, EGL in order
+ # Setup SDL, wm, EGL in order
redef fun setup
do
if debug_gamnit then print "Setting up SDL"
- self.sdl_display = setup_sdl(requested_width, requested_height)
+ self.sdl_window = setup_sdl(requested_width, requested_height)
- if debug_gamnit then print "Setting up X11"
- var x11_display = setup_x11
- var window_handle = window_handle
- setup_egl_display x11_display
+ if debug_gamnit then print "Setting up window manager"
+ setup_egl_display sdl_window.wm_info.display_handle
if debug_gamnit then print "Setting up EGL context"
select_egl_config(red_bits, green_bits, blue_bits, 8, 8, 0, 0)
- setup_egl_context window_handle
+ setup_egl_context sdl_window.wm_info.window_handle
end
# Close EGL and SDL in reverse order of `setup` (nothing to do for X11)
# SDL
# The SDL display managing the window and events
- var sdl_display: SDLDisplay is noautoinit
+ var sdl_window: SDLWindow is noautoinit
+
+ # Title of the window, must be set before creating the window (or redef)
+ var window_title = "gamnit game" is lazy, writable
# Setup the SDL display and lib
- fun setup_sdl(window_width, window_height: Int): SDLDisplay
+ fun setup_sdl(window_width, window_height: Int): SDLWindow
do
- var sdl_display = new SDLDisplay(window_width, window_height)
- assert not sdl_display.address_is_null else print "Opening SDL display failed"
- return sdl_display
- end
-
- # Close the SDL display
- fun close_sdl do sdl_display.destroy
+ assert sdl.initialize((new SDLInitFlags).video) else
+ print_error "Failed to initialize SDL2: {sdl.error}"
+ end
- # Get a native handle to the current SDL window
- fun window_handle: Pointer
- do
- var sdl_wm_info = new SDLSystemWindowManagerInfo
- return sdl_wm_info.x11_window_handle
- end
+ var img_flags = (new SDLImgInitFlags).png.jpg
+ assert sdl.img.initialize(img_flags) == img_flags else
+ print_error "Failed to initialize SDL2 IMG: {sdl.error}"
+ end
- # ---
- # X11
+ var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, (new SDLWindowFlags).opengl)
+ assert not sdl_window.address_is_null else
+ print_error "Failed to create SDL2 window: {sdl.error}"
+ end
- # Get a native handle to the current X11 display
- fun setup_x11: Pointer
- do
- var x11_display = x_open_default_display
- assert not x11_display.address_is_null else print "Opening X11 display failed"
- return x11_display
+ return sdl_window
end
+
+ # Close the SDL display
+ fun close_sdl do sdl_window.destroy
end
redef class TextureAsset
redef fun load_from_platform
do
- var path = "assets" / path # TODO use app.assets_dir
- var sdl_tex = new SDLImage.from_file(path)
+ var path = "assets" / path
- if sdl_tex.address_is_null then
+ var surface = new SDLSurface.load(path.to_cstring)
+ if surface.address_is_null then
error = new Error("Failed to load texture at '{path}'")
return
end
- self.width = sdl_tex.width.to_f
- self.height = sdl_tex.height.to_f
- var format = if sdl_tex.amask > 0 then gl_RGBA else gl_RGB
- var pixels = sdl_tex.pixels
+ self.width = surface.w.to_f
+ self.height = surface.h.to_f
+ var format = if surface.format.amask > 0u32 then gl_RGBA else gl_RGB
+ var pixels = surface.pixels
- load_from_pixels(pixels, sdl_tex.width, sdl_tex.height, format)
+ load_from_pixels(pixels, surface.w, surface.h, format)
end
end
# Support services for Gamnit on GNU/Linux
module gamnit_linux
+import sdl2::events
+
intrude import gamnit
-import display_linux
redef class App
+ private var sdl_event_buffer = new SDLEventBuffer.malloc
+
redef fun feed_events
do
var display = display
if display == null then return
- var events = display.sdl_display.events
- display.check_lock_cursor
- for event in events do accept_event(event)
+ loop
+ var avail = sdl_event_buffer.poll_event
+ if not avail then break
+
+ # Convert to an SDL event with data
+ var sdl_event = sdl_event_buffer.to_event
+ # Convert to `mnit::inputs` conforming objects
+ var gamnit_event = sdl_event.to_gamnit_event(sdl_event_buffer)
+ accept_event gamnit_event
+ end
end
end
-redef class GamnitDisplay
+# ---
+# Redef services from `sdl2::events`
+
+redef class SDLEvent
+ private fun to_gamnit_event(buffer: SDLEventBuffer): GamnitInputEvent
+ do return new GamnitOtherEvent(buffer, self)
+end
+
+redef class SDLQuitEvent
+ redef fun to_gamnit_event(buffer) do return new GamnitQuitEvent(buffer, self)
+end
+
+redef class SDLMouseEvent
+ redef fun to_gamnit_event(buffer) do return new GamnitPointerEvent(buffer, self)
+end
+
+redef class SDLKeyboardEvent
+ redef fun to_gamnit_event(buffer) do return new GamnitKeyEvent(buffer, self)
+end
+
+# ---
+# Implement `mnit::inputs` interfaces
+
+# SDL2 event wrapper implementing the `mnit::input` API
+#
+# There is two views to the underlying native object: `buffer` and `native`.
+class GamnitInputEvent
+ super InputEvent
+
+ # Native SDL 2 event buffer with the pseudo class hierarchy metadata
+ var buffer: SDLEventBuffer
+
+ # Native SDL 2 event
+ var native: NATIVE
+
+ # Type of the `native` underlying SDL 2 event
+ type NATIVE: SDLEvent
+end
+
+# Event on user requested quit
+class GamnitQuitEvent
+ super GamnitInputEvent
+ super QuitEvent
+
+ redef type NATIVE: SDLQuitEvent
+end
+
+# Event on keyboard input
+class GamnitKeyEvent
+ super GamnitInputEvent
+ super KeyEvent
+
+ redef type NATIVE: SDLKeyboardEvent
+
+ redef fun is_down do return buffer.is_keydown
+ redef fun is_up do return buffer.is_keyup
+ redef fun is_arrow_up do return native.keysym.is_up
+ redef fun is_arrow_left do return native.keysym.is_left
+ redef fun is_arrow_down do return native.keysym.is_down
+ redef fun is_arrow_right do return native.keysym.is_right
+ redef fun to_c do return native.to_s.chars.first
+ redef fun name do return native.to_s.to_lower
+end
+
+# Event on pointer, mouse and finger input
+class GamnitPointerEvent
+ super GamnitInputEvent
+ super PointerEvent
+
+ redef type NATIVE: SDLMouseEvent
- # HACK to keep the cursor in the center of the screen without producing move events
- private fun check_lock_cursor
+ redef fun x do return native.x.to_f
+ redef fun y do return native.y.to_f
+ redef fun is_move do return buffer.is_mouse_motion
+
+ redef fun pressed
do
- var sdl_display = sdl_display
- if lock_cursor and sdl_display.input_focus then
- sdl_display.ignore_mouse_motion_events = true
- sdl_display.warp_mouse(width/2, height/2)
- sdl_display.ignore_mouse_motion_events = false
- end
+ var native = native
+ if native isa SDLMouseButtonEvent then
+ return native.pressed and native.button == 1
+ else if native isa SDLMouseMotionEvent then
+ return native.state & 1 == 1
+ else abort
end
end
+
+# SDL2 event not handled by gamnit
+class GamnitOtherEvent
+ super GamnitInputEvent
+end