Created
June 1, 2026 14:14
-
-
Save skinnyjames/fd06d079ee64e422ce54e778673cfefc to your computer and use it in GitHub Desktop.
launcher code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module HackOS | |
| class MovingIcon < Hokusai::Block | |
| template <<~EOF | |
| [template] | |
| virtual | |
| EOF | |
| computed! :moving | |
| computed! :name | |
| computed! :width | |
| computed! :height | |
| computed :padding, default: Hokusai::Padding.new(0.0, 0.0, 0.0, 0.0), convert: Hokusai::Padding | |
| def vertex | |
| <<-EOF | |
| #version 100 | |
| attribute vec3 vertexPosition; | |
| attribute vec2 vertexTexCoord; | |
| attribute vec4 vertexColor; | |
| uniform mat4 mvp; | |
| uniform float uTime; | |
| uniform int uIsHeld; | |
| varying vec2 fragTexCoord; | |
| varying vec4 fragColor; | |
| void main() { | |
| fragTexCoord = vertexTexCoord; | |
| fragColor = vertexColor; | |
| vec3 pos = vertexPosition; | |
| if (uIsHeld == 1) { | |
| // Warp the X and Y bounds slightly based on time to simulate jiggliness | |
| pos.x += sin(pos.y * 0.1 + uTime * 15.0) * 4.0; | |
| pos.y += cos(pos.x * 0.1 + uTime * 12.0) * 2.0; | |
| } | |
| gl_Position = mvp * vec4(pos, 1.0); | |
| } | |
| EOF | |
| end | |
| def fragment | |
| <<-EOF | |
| #version 100 | |
| precision mediump float; | |
| varying vec2 fragTexCoord; | |
| varying vec4 fragColor; | |
| uniform sampler2D texture0; | |
| void main() { | |
| gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; | |
| } | |
| EOF | |
| end | |
| def glitch | |
| <<-EOF | |
| #version 100 | |
| precision mediump float; | |
| varying vec2 fragTexCoord; | |
| varying vec4 fragColor; | |
| uniform sampler2D texture0; | |
| uniform float uTime; | |
| uniform int uIsHeld; | |
| // Simple pseudo-random generator | |
| float rand(vec2 co) { | |
| return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); | |
| } | |
| void main() { | |
| if (uIsHeld == 1) { | |
| // Create an intermittent horizontal slicing glitch triggered by time | |
| float glitchThreshold = step(0.85, sin(uTime * 10.0)); | |
| float sliceY = floor(fragTexCoord.y * 10.0); // Split button into 10 bands | |
| vec2 uvOffset = vec2(0.0); | |
| if (glitchThreshold > 0.0) { | |
| // Apply unique horizontal displacement per vertical slice band | |
| uvOffset.x = (rand(vec2(sliceY, uTime)) - 0.5) * 0.08; | |
| } | |
| // Sample separate channels with offsets to create Chromatic Aberration splits | |
| float r = texture2D(texture0, fragTexCoord + uvOffset).r; | |
| float g = texture2D(texture0, fragTexCoord).g; | |
| float b = texture2D(texture0, fragTexCoord - uvOffset).b; | |
| float a = texture2D(texture0, fragTexCoord + uvOffset).a; | |
| // Inject subtle matrix lines | |
| float scanline = sin(fragTexCoord.y * 120.0) * 0.15 + 0.85; | |
| gl_FragColor = vec4(vec3(r, g, b) * scanline, a) * fragColor; | |
| } else { | |
| gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; | |
| } | |
| } | |
| EOF | |
| end | |
| def render(canvas) | |
| @time = Hokusai.monotonic - @start | |
| draw do | |
| if img = Hokusai.images.get(name) | |
| draw do | |
| if moving == "t" | |
| uniforms = { | |
| "uIsHeld" => [1, HP_SHADER_UNIFORM_INT], | |
| "uTime" => [@time, HP_SHADER_UNIFORM_FLOAT] | |
| } | |
| shader_begin do |command| | |
| # command.vertex_shader = vertex | |
| command.fragment_shader = glitch | |
| command.uniforms = uniforms | |
| end | |
| end | |
| image(img, canvas.x + padding.left, canvas.y + padding.top, (width&.to_f || canvas.width) - padding.right, (height&.to_f || canvas.height) - padding.bottom) | |
| shader_end if moving | |
| end | |
| end | |
| end | |
| yield canvas | |
| end | |
| def initialize(**args) | |
| @start = Hokusai.monotonic | |
| super | |
| end | |
| end | |
| class Homescreen < Hokusai::Block | |
| style <<-EOF | |
| [style] | |
| appIcon { | |
| width: 228.0; | |
| height: 228.0; | |
| padding: padding(50.0, 50.0, 50.0, 50.0); | |
| } | |
| container { | |
| padding: padding(80.0, 0.0, 0.0, 80.0); | |
| } | |
| EOF | |
| template do | |
| child(Hokusai::Blocks::Vblock) do | |
| on :taprelease do | |
| if pressing | |
| self.pressing = nil | |
| control.theme.save | |
| end | |
| end | |
| child(Hokusai::Blocks::Vblock) do | |
| static :height, "300.0" | |
| child(Hokusai::Blocks::Empty) do | |
| end | |
| end | |
| child(Hokusai::Blocks::Hblock) do | |
| static :wrap, "true" | |
| static :width, "684.0" | |
| each_child(MovingIcon, :apps) do |app| | |
| merge_styles "appIcon" | |
| prop :key do | |
| "app-#{app.value["name"]}" | |
| end | |
| prop :name do | |
| "icon-#{app.value["name"]}" | |
| end | |
| prop :moving do | |
| pressing && pressing["name"] == app.value["name"] ? "t" : "f" | |
| end | |
| prop :width do | |
| pressing && pressing["name"] == app.value["name"] ? 240.0 : 228.0 | |
| end | |
| prop :height do | |
| pressing && pressing["name"] == app.value["name"] ? 240.0 : 228.0 | |
| end | |
| # on :tap do | |
| # emit("open", app.value["name"]) | |
| # end | |
| on :taphold do |event| | |
| if event.duration > 0.3 | |
| p "taphold" | |
| control.dbus.feedback.button(1) | |
| self.pressing = app.value | |
| end | |
| end | |
| on :tapdown do |event| | |
| if pressing | |
| move(pressing["name"], app.value["name"]) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| inject :theme | |
| inject :control | |
| attr_accessor :pressing | |
| def initialize(**args) | |
| @pressing = nil | |
| super | |
| end | |
| def move(from, to) | |
| return if from == to | |
| targetidx = apps.index { |app| app["name"] == to } | |
| app = apps.find{ |app| app["name"] == from } | |
| apps.delete(app) | |
| apps.insert(targetidx, app) | |
| end | |
| def apps | |
| theme.apps | |
| end | |
| def render(canvas) | |
| image(Hokusai.images.get("wallpaper"), canvas.x, canvas.y, canvas.width, canvas.height) do |command| | |
| end | |
| yield canvas | |
| end | |
| end | |
| class Launcher < Hokusai::Block | |
| template <<-EOF | |
| [template] | |
| vblock | |
| [if="active"] | |
| variable { | |
| :klass="active" | |
| @close="remove" | |
| @swipe="handle_swipe" | |
| } | |
| [else] | |
| homescreen { @open="launch" } | |
| EOF | |
| uses( | |
| vblock: Hokusai::Blocks::Vblock, | |
| variable: Variable, | |
| homescreen: Homescreen | |
| ) | |
| inject :control | |
| inject :theme | |
| inject :dbus | |
| attr_reader :currents | |
| attr_accessor :active | |
| def handle_swipe(event) | |
| if event.swipe_direction == :up | |
| self.active = nil | |
| end | |
| end | |
| def launch(app) | |
| if current = currents[app] | |
| self.active = current | |
| else | |
| # mount and setup events | |
| self.active = begin | |
| config = control.theme.apps.find do |config| | |
| config["name"] == app | |
| end | |
| klass = eval config["class"] | |
| klass.provides.merge!(@injections) | |
| obj = klass.mount("root", node) | |
| obj | |
| end | |
| currents[app] = active | |
| end | |
| end | |
| def initialize(**args) | |
| @currents = {} | |
| @active = nil | |
| super | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment