.{math, log, string, random, buffer, memory} := @use("stn"); .{Color, Surface, new_surface, put_surface, sync, put_rect, put_filled_rect, text, put_text, clear, white, black} := @use("lib:render"); .{Channel, Window, WindowProps, WindowData, MessageHeader, BUFFER_SERVER, BUFFER_CLIENT, message, permissions, recv_header, recv_message, send_message, send_header, await_message} := @use("lib:sunset_proto") WindowServer := struct { window_count: uint, channel: Channel, // ! replace this with a collection when we get an allocator windows: [?Window; 10], font: text.Font, } // ! in the future this should be safely handled server := @as(WindowServer, idk) psf := @embed("../../../assets/consolefonts/tamsyn/10x20r.psf") start := fn(): void { font := text.font_from_psf2(@bitcast(&psf), false) if font == null { log.error("server: failed to load asset\0") return } server = .( 0, .{client: buffer.create(BUFFER_CLIENT), server: buffer.create(BUFFER_SERVER)}, .(null, null, null, null, null, null, null, null, null, null), @as(text.Font, font), ) log.info("server: started server\0") } incoming := fn(): bool { msg := recv_header(server.channel.server) if msg == null { return true } if msg.kind == message.syn { log.info("server: recv syn\0") channel := Channel.(buffer.create_nameless(), buffer.create_nameless()) send_message(Channel, message.ack, channel, server.channel.client) props := await_message(WindowProps, channel.server) if props.header.kind != message.props { return true } log.info("server: recv props\0") // ! do inspection of requested props here data := WindowData.(props.body, channel, permissions.default) send_message(WindowData, message.ack, data, channel.client) surface := new_window_decorations(data.props.dimensions) // decorations { title := data.props.title title_length := string.length(title) deco_length := title_length * 10 // draw the window tab bar put_filled_rect(surface, .(0, 0), .(data.props.dimensions.x + DECO_WIDTH + deco_length, DECO_HEIGHT_TOP), DECO_COLOUR) // Draw the window tab put_filled_rect(surface, .(0, 0), .(deco_length, DECO_HEIGHT_TOP - 1), DECO_COLOUR_DARKER) // Draw the outside box put_rect(surface, .(0, 0), data.props.dimensions + .(DECO_WIDTH - 1, DECO_HEIGHT_TOP + DECO_HEIGHT_BOTTOM - 1), DECO_COLOUR) put_text(surface, server.font, .(2, 1), .(0, 0, 0, 255), data.props.title) } server.windows[server.window_count] = .(data, surface) server.window_count += 1 } return true } $DECO_WIDTH := 2 $DECO_HEIGHT_TOP := 20 $DECO_HEIGHT_BOTTOM := 1 $DECO_COLOUR := Color.(100, 200, 255, 255) $DECO_COLOUR_DARKER := Color.(89, 57, 89, 255) new_window_decorations := fn(dimensions: math.Vec2(uint)): Surface { return new_surface( dimensions.x + DECO_WIDTH, dimensions.y + DECO_HEIGHT_TOP + DECO_HEIGHT_BOTTOM, ) } // ! compositor code. this currently disallows tearing. collect_frames := fn(): void { i := 0 loop if i == 10 break else { window := server.windows[i] if window == null { i += 1 continue } header := recv_header(window.data.channel.server) if header == null { i += 1 continue } if header.kind != message.frame_ready { i += 1 continue } send_header(message.ack, window.data.channel.client) ptr := await_message(^Color, window.data.channel.server) if ptr.header.kind != message.ack { return } put_surface( window.surface, Surface.( ptr.body, window.data.props.dimensions.x, window.data.props.dimensions.y, window.data.props.dimensions.x * window.data.props.dimensions.y, ), .(DECO_WIDTH / 2, DECO_HEIGHT_TOP), false, ) i += 1 } } render_clients := fn(screen: Surface): void { i := 0 loop if i == 10 break else { window := server.windows[i] if window == null { i += 1 continue } put_surface(screen, window.surface, window.data.props.position, false) i += 1 } }