diff --git a/prosperon/draw2d.cm b/prosperon/draw2d.cm index 7ee6286d..6cdcdf9c 100644 --- a/prosperon/draw2d.cm +++ b/prosperon/draw2d.cm @@ -102,13 +102,13 @@ draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, def draw.line(wing2, defl, material) } -draw.rectangle = function render_rectangle(rect, defl, material) { +draw.rectangle = function render_rectangle(rect, defl, material = {color:{r:1,g:1,b:1,a:1}}) { var opt = defl ? {...rect_def, ...defl} : rect_def add_command("draw_rect", { - rect: rect, - opt: opt, - material: material + rect, + opt, + material }) } diff --git a/prosperon/prosperon.cm b/prosperon/prosperon.cm index 02f19a21..fe0cad93 100644 --- a/prosperon/prosperon.cm +++ b/prosperon/prosperon.cm @@ -23,22 +23,19 @@ function makeOrthoMetal(l,r,b,t,n,f){ } function make_camera_pblob(camera) { - def zoom = camera.zoom; - // Use surface dimensions if rendering to a surface, otherwise window dimensions - def cw = camera.surface ? camera.surface.width : win_size.width; - def ch = camera.surface ? camera.surface.height : win_size.height; - // how big is the world window? - def world_w = cw / zoom; - def world_h = ch / zoom; + def cw = camera.surface ? camera.surface.width : win_size.width; + def ch = camera.surface ? camera.surface.height : win_size.height; + + def zoom = camera.zoom || ch + + def world_h = zoom; + def world_w = zoom * cw / ch; - // compute world‐space bounds so that camera.pos lands at the anchor - // anchor.x = 0 → origin at left; 1 → origin at right def l = camera.pos[0] - camera.anchor[0] * world_w; def b = camera.pos[1] - camera.anchor[1] * world_h; def r = l + world_w; def t = b + world_h; - // now build the Metal/Vulkan‐style ortho (z ∈ [0,1]) def mat = makeOrthoMetal(l, r, b, t, 0, 1); return geometry.array_blob(mat); } @@ -150,6 +147,29 @@ win_config.__proto__ = default_window win_config.metal = true var window = new video.window(win_config) +var win_proto = window.__proto__ +win_proto.toJSON = function() +{ + var flags = this.flags + var ret = { + title: this.title, + size: this.size, + pixel_size: this.sizeInPixels, + display_scale: this.displayScale, + pixel_density: this.pixelDensity, + pos: this.position, + opacity: this.opacity, + fullscreen: this.fullscreen, + safe_area: this.safe_area(), + } + + for (var i in flags) + ret[i] = flags[i] + return ret +} + +window.resizable = true + var device = new sdl_gpu.gpu({ shaders_msl:true, shaders_metallib:true, @@ -158,6 +178,15 @@ var device = new sdl_gpu.gpu({ device.claim_window(window) device.set_swapchain(window, 'sdr', 'vsync') +var white_pixel = { + width:1, + height:1, + pixels: new blob(32, true), // 32 bits, all set to 1 for a white blob + pitch:32 +} + +stone(white_pixel.pixels) + var shader_type = device.shader_format()[0] shader_type = 'msl' @@ -398,24 +427,6 @@ function poll_input() { } if (shouldInclude) { - // Transform mouse coordinates from window to renderer coordinates - if (event.pos && (event.type == 'mouse_motion' || - event.type == 'mouse_button_down' || - event.type == 'mouse_button_up' || - event.type == 'mouse_wheel')) { - // Convert window coordinates to renderer logical coordinates -// var logicalPos = renderer.coordsFromWindow(event.pos) -// event.pos = logicalPos - } - // Handle drop events which also have position - if (event.pos && (event.type == 'drop_file' || - event.type == 'drop_text' || - event.type == 'drop_position')) { -// var logicalPos = renderer.coordsFromWindow(event.pos) -// event.pos = logicalPos - } - - // Handle window events if (event.type == 'window_pixel_size_changed') { win_size.width = event.width win_size.height = event.height @@ -431,7 +442,6 @@ function poll_input() { if (event.type.startsWith('mouse_') && event.pos && event.pos.y) event.pos.y = -event.pos.y + win_size.height -// event.pos.y = -event.pos.y + logical.height filteredEvents.push(event) } @@ -519,7 +529,7 @@ function get_img_gpu(surface) size: surface.pixels.length/8, usage: "upload" }) - + tbuf.copy_blob(device, surface.pixels) copy_pass.upload_to_texture({ @@ -537,7 +547,8 @@ function get_img_gpu(surface) d: 1 }, false); - new_tex.push(gpu) + if (full_mip > 1) + new_tex.push(gpu) return gpu } @@ -670,6 +681,21 @@ cmd_fns.draw_slice9 = function(cmd) render_geom(mesh, img) } +cmd_fns.draw_rect = function(cmd) +{ + // Create geometry for a rectangle quad + var geom = geometry.make_rect_quad(cmd.rect, null, cmd.material.color || [1,1,1,1]) + geom.indices = geometry.make_quad_indices(1) + geom.num_indices = 6 + + // Use white_pixel as the texture so the color modulation works + if (!white_pixel[GPU]) { + white_pixel[GPU] = get_img_gpu(white_pixel) + } + + render_geom(geom, {[GPU]: white_pixel[GPU]}) +} + var copy_pass prosperon.create_batch = function create_batch(draw_cmds, done) { @@ -806,7 +832,6 @@ function poll_file_changes() { } }) - // Schedule next poll in 0.5 seconds $_.delay(poll_file_changes, 0.5) } diff --git a/prosperon/tilemap.cm b/prosperon/tilemap.cm index 457a3a09..7f2cbf78 100644 --- a/prosperon/tilemap.cm +++ b/prosperon/tilemap.cm @@ -5,8 +5,7 @@ function tilemap() this.tiles = []; this.offset_x = 0; this.offset_y = 0; - this.size_x = 32; - this.size_y = 32; + this.ppu = 32 this.layer = 0; // Default layer for scene tree sorting this._geometry_cache = {}; // Cache actual geometry data by texture this._dirty = true; @@ -113,13 +112,17 @@ tilemap.prototype = var textureKey = imageToKey.get(tile); if (!textureGroups[textureKey]) { + // compute world-unit size of one tile of this texture + var tile_wu = tile.width / this.ppu; + var tile_hu = tile.height / this.ppu; + textureGroups[textureKey] = { tiles: [], image: tile, // Store the image object offset_x: this.offset_x, offset_y: this.offset_y, - size_x: this.size_x, - size_y: this.size_y + size_x: tile_wu, + size_y: tile_hu }; } textureGroups[textureKey].tiles.push({ @@ -142,7 +145,7 @@ tilemap.prototype = tiles: [], offset_x: group.offset_x, offset_y: group.offset_y, - size_x: group.size_x, + size_x: group.size_x, // now in world-units size_y: group.size_y, pos_x: pos.x, pos_y: pos.y diff --git a/source/qjs_geometry.c b/source/qjs_geometry.c index 0ebcd4ad..01f63916 100644 --- a/source/qjs_geometry.c +++ b/source/qjs_geometry.c @@ -1428,9 +1428,8 @@ JSC_CCALL(geometry_make_rect_quad, uv_data[6] = uv.x + uv.w; uv_data[7] = uv.y; // top-right // Set colors - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) color_data[i] = color; - } // Create result object ret = JS_NewObject(js); diff --git a/source/qjs_sdl_input.c b/source/qjs_sdl_input.c index 812bb959..4075bb66 100644 --- a/source/qjs_sdl_input.c +++ b/source/qjs_sdl_input.c @@ -319,10 +319,18 @@ static int event2wota_count_props(const SDL_Event *event) count += 1; break; - /* Window events (just group them all together) */ + /* Window events that only need 'which' */ + case SDL_EVENT_WINDOW_EXPOSED: + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + // which => 1 extra + count += 1; + break; + + /* Window events that need data1 and data2 */ case SDL_EVENT_WINDOW_SHOWN: case SDL_EVENT_WINDOW_HIDDEN: - case SDL_EVENT_WINDOW_EXPOSED: case SDL_EVENT_WINDOW_MOVED: case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: @@ -332,14 +340,10 @@ static int event2wota_count_props(const SDL_Event *event) case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_MOUSE_ENTER: case SDL_EVENT_WINDOW_MOUSE_LEAVE: - case SDL_EVENT_WINDOW_FOCUS_GAINED: - case SDL_EVENT_WINDOW_FOCUS_LOST: - case SDL_EVENT_WINDOW_CLOSE_REQUESTED: case SDL_EVENT_WINDOW_HIT_TEST: case SDL_EVENT_WINDOW_ICCPROF_CHANGED: case SDL_EVENT_WINDOW_DISPLAY_CHANGED: case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: - case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: case SDL_EVENT_WINDOW_OCCLUDED: case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: @@ -349,6 +353,11 @@ static int event2wota_count_props(const SDL_Event *event) count += 3; break; + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: + // which, x, y, width, height => 5 extra + count += 5; + break; + case SDL_EVENT_JOYSTICK_ADDED: case SDL_EVENT_JOYSTICK_REMOVED: case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: @@ -557,17 +566,20 @@ static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) { wota_write_text(wb, "owner"); wota_write_sym(wb, e->clipboard.owner ? WOTA_TRUE : WOTA_FALSE); break; + case SDL_EVENT_WINDOW_EXPOSED: + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->window.windowID); + break; case SDL_EVENT_WINDOW_SHOWN: case SDL_EVENT_WINDOW_HIDDEN: - case SDL_EVENT_WINDOW_EXPOSED: case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_MOUSE_ENTER: case SDL_EVENT_WINDOW_MOUSE_LEAVE: - case SDL_EVENT_WINDOW_FOCUS_GAINED: - case SDL_EVENT_WINDOW_FOCUS_LOST: - case SDL_EVENT_WINDOW_CLOSE_REQUESTED: case SDL_EVENT_WINDOW_HIT_TEST: case SDL_EVENT_WINDOW_ICCPROF_CHANGED: case SDL_EVENT_WINDOW_OCCLUDED: @@ -575,7 +587,6 @@ static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) { case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: case SDL_EVENT_WINDOW_DESTROYED: case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: - case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: wota_write_text(wb, "which"); wota_write_number(wb, (double)e->window.windowID); wota_write_text(wb, "data1"); @@ -583,6 +594,32 @@ static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) { wota_write_text(wb, "data2"); wota_write_number(wb, (double)e->window.data2); break; + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: + { + SDL_Window *window = SDL_GetWindowFromID(e->window.windowID); + SDL_Rect safe_area = {0, 0, 0, 0}; + if (window && SDL_GetWindowSafeArea(window, &safe_area)) { + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->window.windowID); + wota_write_text(wb, "x"); + wota_write_number(wb, (double)safe_area.x); + wota_write_text(wb, "y"); + wota_write_number(wb, (double)safe_area.y); + wota_write_text(wb, "width"); + wota_write_number(wb, (double)safe_area.w); + wota_write_text(wb, "height"); + wota_write_number(wb, (double)safe_area.h); + } else { + // Fallback to original behavior if SDL_GetWindowSafeArea fails + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->window.windowID); + wota_write_text(wb, "data1"); + wota_write_number(wb, (double)e->window.data1); + wota_write_text(wb, "data2"); + wota_write_number(wb, (double)e->window.data2); + } + } + break; case SDL_EVENT_WINDOW_MOVED: wota_write_text(wb, "which"); wota_write_number(wb, (double)e->window.windowID); diff --git a/source/qjs_sdl_video.c b/source/qjs_sdl_video.c index 6ad4688e..75025883 100644 --- a/source/qjs_sdl_video.c +++ b/source/qjs_sdl_video.c @@ -286,9 +286,11 @@ JSValue js_window_theme(JSContext *js, JSValue self) JSValue js_window_safe_area(JSContext *js, JSValue self) { SDL_Window *w = js2SDL_Window(js,self); - rect r; + SDL_Rect r; SDL_GetWindowSafeArea(w, &r); - return rect2js(js,r); + rect newr; + SDL_RectToFRect(&r, &newr); + return rect2js(js,newr); } JSValue js_window_bordered(JSContext *js, JSValue self, int argc, JSValue *argv) @@ -700,7 +702,36 @@ JSValue js_window_updateSurfaceRects(JSContext *js, JSValue self, int argc, JSVa JSValue js_window_get_flags(JSContext *js, JSValue self) { SDL_Window *w = js2SDL_Window(js,self); - return number2js(js, SDL_GetWindowFlags(w)); + SDL_WindowFlags flags = SDL_GetWindowFlags(w); + + JSValue ret = JS_NewObject(js); + JS_SetPropertyStr(js, ret, "fullscreen", JS_NewBool(js, flags & SDL_WINDOW_FULLSCREEN)); + JS_SetPropertyStr(js, ret, "opengl", JS_NewBool(js, flags & SDL_WINDOW_OPENGL)); + JS_SetPropertyStr(js, ret, "occluded", JS_NewBool(js, flags & SDL_WINDOW_OCCLUDED)); + JS_SetPropertyStr(js, ret, "hidden", JS_NewBool(js, flags & SDL_WINDOW_HIDDEN)); + JS_SetPropertyStr(js, ret, "borderless", JS_NewBool(js, flags & SDL_WINDOW_BORDERLESS)); + JS_SetPropertyStr(js, ret, "resizable", JS_NewBool(js, flags & SDL_WINDOW_RESIZABLE)); + JS_SetPropertyStr(js, ret, "minimized", JS_NewBool(js, flags & SDL_WINDOW_MINIMIZED)); + JS_SetPropertyStr(js, ret, "maximized", JS_NewBool(js, flags & SDL_WINDOW_MAXIMIZED)); + JS_SetPropertyStr(js, ret, "mouseGrabbed", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_GRABBED)); + JS_SetPropertyStr(js, ret, "inputFocus", JS_NewBool(js, flags & SDL_WINDOW_INPUT_FOCUS)); + JS_SetPropertyStr(js, ret, "mouseFocus", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_FOCUS)); + JS_SetPropertyStr(js, ret, "external", JS_NewBool(js, flags & SDL_WINDOW_EXTERNAL)); + JS_SetPropertyStr(js, ret, "modal", JS_NewBool(js, flags & SDL_WINDOW_MODAL)); + JS_SetPropertyStr(js, ret, "highPixelDensity", JS_NewBool(js, flags & SDL_WINDOW_HIGH_PIXEL_DENSITY)); + JS_SetPropertyStr(js, ret, "mouseCapture", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_CAPTURE)); + JS_SetPropertyStr(js, ret, "mouseRelativeMode", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)); + JS_SetPropertyStr(js, ret, "alwaysOnTop", JS_NewBool(js, flags & SDL_WINDOW_ALWAYS_ON_TOP)); + JS_SetPropertyStr(js, ret, "utility", JS_NewBool(js, flags & SDL_WINDOW_UTILITY)); + JS_SetPropertyStr(js, ret, "tooltip", JS_NewBool(js, flags & SDL_WINDOW_TOOLTIP)); + JS_SetPropertyStr(js, ret, "popupMenu", JS_NewBool(js, flags & SDL_WINDOW_POPUP_MENU)); + JS_SetPropertyStr(js, ret, "keyboardGrabbed", JS_NewBool(js, flags & SDL_WINDOW_KEYBOARD_GRABBED)); + JS_SetPropertyStr(js, ret, "vulkan", JS_NewBool(js, flags & SDL_WINDOW_VULKAN)); + JS_SetPropertyStr(js, ret, "metal", JS_NewBool(js, flags & SDL_WINDOW_METAL)); + JS_SetPropertyStr(js, ret, "transparent", JS_NewBool(js, flags & SDL_WINDOW_TRANSPARENT)); + JS_SetPropertyStr(js, ret, "notFocusable", JS_NewBool(js, flags & SDL_WINDOW_NOT_FOCUSABLE)); + + return ret; } JSValue js_window_sync(JSContext *js, JSValue self, int argc, JSValue *argv)