diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md diff --git a/SECURITY.md b/.github/SECURITY.md similarity index 100% rename from SECURITY.md rename to .github/SECURITY.md diff --git a/assets/demo.gif b/.github/assets/demo.gif similarity index 100% rename from assets/demo.gif rename to .github/assets/demo.gif diff --git a/.gitignore b/.gitignore index ba6b563..7ec45a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -result/ -hexecute +# go build -o bin ./... +bin/ + +# nix build (produces a symlink) +result diff --git a/README.md b/README.md index a56766e..8a04103 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A gesture-based launcher for Wayland. Launch apps by casting spells! 🪄 -![Demo GIF](assets/demo.gif) +![Demo GIF](.github/assets/demo.gif) ## Installation @@ -58,8 +58,9 @@ If you have [Nix](https://nixos.org/) installed, simply run `nix build`. Otherwise, make sure you have Go (and all dependent Wayland (and X11!?) libs) installed, then run: ```bash -go build -./hexecute +mkdir -p bin +go build -o bin ./... +./bin/hexecute ``` ## Usage diff --git a/cmd/hexecute/main.go b/cmd/hexecute/main.go new file mode 100644 index 0000000..2e2f13c --- /dev/null +++ b/cmd/hexecute/main.go @@ -0,0 +1,214 @@ +package main + +import ( + "encoding/json" + "flag" + "log" + "os" + "runtime" + "time" + + "github.com/ThatOtherAndrew/Hexecute/internal/config" + "github.com/ThatOtherAndrew/Hexecute/internal/draw" + "github.com/ThatOtherAndrew/Hexecute/internal/execute" + gestures "github.com/ThatOtherAndrew/Hexecute/internal/gesture" + "github.com/ThatOtherAndrew/Hexecute/internal/models" + "github.com/ThatOtherAndrew/Hexecute/internal/opengl" + "github.com/ThatOtherAndrew/Hexecute/internal/spawn" + "github.com/ThatOtherAndrew/Hexecute/internal/stroke" + "github.com/ThatOtherAndrew/Hexecute/internal/update" + "github.com/ThatOtherAndrew/Hexecute/pkg/wayland" + "github.com/go-gl/gl/v4.1-core/gl" +) + +func init() { + runtime.LockOSThread() +} + +type App struct { + *models.App +} + +func main() { + learnCommand := flag.String("learn", "", "Learn a new gesture for the specified command") + listGestures := flag.Bool("list", false, "List all registered gestures") + removeGesture := flag.String("remove", "", "Remove a gesture by command name") + flag.Parse() + + if flag.NArg() > 0 { + log.Fatalf("Unknown arguments: %v", flag.Args()) + } + + if *listGestures { + gestures, err := gestures.LoadGestures() + if err != nil { + log.Fatal("Failed to load gestures:", err) + } + if len(gestures) == 0 { + println("No gestures registered") + } else { + println("Registered gestures:") + for _, g := range gestures { + println(" ", g.Command) + } + } + return + } + + if *removeGesture != "" { + gestures, err := gestures.LoadGestures() + if err != nil { + log.Fatal("Failed to load gestures:", err) + } + + found := false + for i, g := range gestures { + if g.Command == *removeGesture { + gestures = append(gestures[:i], gestures[i+1:]...) + found = true + break + } + } + + if !found { + log.Fatalf("Gesture not found: %s", *removeGesture) + } + + configFile, err := config.GetPath() + if err != nil { + log.Fatal("Failed to get config path:", err) + } + + data, err := json.Marshal(gestures) + if err != nil { + log.Fatal("Failed to marshal gestures:", err) + } + + if err := os.WriteFile(configFile, data, 0644); err != nil { + log.Fatal("Failed to save gestures:", err) + } + + println("Removed gesture:", *removeGesture) + return + } + + window, err := wayland.NewWaylandWindow() + if err != nil { + log.Fatal("Failed to create Wayland window:", err) + } + defer window.Destroy() + + app := &models.App{StartTime: time.Now()} + + if *learnCommand != "" { + app.LearnMode = true + app.LearnCommand = *learnCommand + log.Printf("Learn mode: Draw the gesture 3 times for command '%s'", *learnCommand) + } else { + gestures, err := gestures.LoadGestures() + if err != nil { + log.Fatal("Failed to load gestures:", err) + } + app.SavedGestures = gestures + log.Printf("Loaded %d gesture(s)", len(gestures)) + } + + opengl := opengl.New(app) + if err := opengl.InitGL(); err != nil { + log.Fatal("Failed to initialize OpenGL:", err) + } + + gl.ClearColor(0, 0, 0, 0) + + for range 5 { + window.PollEvents() + gl.Clear(gl.COLOR_BUFFER_BIT) + window.SwapBuffers() + } + + x, y := window.GetCursorPos() + app.LastCursorX = float32(x) + app.LastCursorY = float32(y) + + lastTime := time.Now() + var wasPressed bool + + for !window.ShouldClose() { + now := time.Now() + dt := float32(now.Sub(lastTime).Seconds()) + lastTime = now + + window.PollEvents() + update := update.New(app) + update.UpdateCursor(window) + + if key, state, hasKey := window.GetLastKey(); hasKey { + if state == 1 && key == 0xff1b { + if !app.IsExiting { + app.IsExiting = true + app.ExitStartTime = time.Now() + window.DisableInput() + x, y := window.GetCursorPos() + spawn := spawn.New(app) + spawn.SpawnExitWisps(float32(x), float32(y)) + } + } + window.ClearLastKey() + } + + if app.IsExiting { + if time.Since(app.ExitStartTime).Seconds() > 0.8 { + break + } + } + isPressed := window.GetMouseButton() + if isPressed && !wasPressed { + app.IsDrawing = true + } else if !isPressed && wasPressed { + app.IsDrawing = false + + if app.LearnMode && len(app.Points) > 0 { + processed := stroke.ProcessStroke(app.Points) + app.LearnGestures = append(app.LearnGestures, processed) + app.LearnCount++ + log.Printf("Captured gesture %d/3", app.LearnCount) + + app.Points = nil + + if app.LearnCount >= 3 { + if err := gestures.SaveGesture(app.LearnCommand, app.LearnGestures); err != nil { + log.Fatal("Failed to save gesture:", err) + } + log.Printf("Gesture saved for command: %s", app.LearnCommand) + + app.IsExiting = true + app.ExitStartTime = time.Now() + window.DisableInput() + x, y := window.GetCursorPos() + spawn := spawn.New(app) + spawn.SpawnExitWisps(float32(x), float32(y)) + } + } else if !app.LearnMode && len(app.Points) > 0 { + x, y := window.GetCursorPos() + exec := execute.New(app) + exec.RecognizeAndExecute(window, float32(x), float32(y)) + app.Points = nil + } + } + wasPressed = isPressed + + if app.IsDrawing { + x, y := window.GetCursorPos() + gesture := gestures.New(app) + gesture.AddPoint(float32(x), float32(y)) + + spawn := spawn.New(app) + spawn.SpawnCursorSparkles(float32(x), float32(y)) + } + + update.UpdateParticles(dt) + drawer := draw.New(app) + drawer.Draw(window) + window.SwapBuffers() + } +} diff --git a/go.mod b/go.mod index 54569a0..8772d7e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module hexecute +module github.com/ThatOtherAndrew/Hexecute go 1.25.1 diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..d2c2ef5 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,18 @@ +package config + +import ( + "os" + "path/filepath" +) + +func GetPath() (string, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + configDir := filepath.Join(homeDir, ".config", "hexecute") + if err := os.MkdirAll(configDir, 0755); err != nil { + return "", err + } + return filepath.Join(configDir, "gestures.json"), nil +} diff --git a/internal/draw/draw.go b/internal/draw/draw.go new file mode 100644 index 0000000..f7a2b83 --- /dev/null +++ b/internal/draw/draw.go @@ -0,0 +1,265 @@ +package draw + +import ( + "math" + "time" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" + "github.com/ThatOtherAndrew/Hexecute/pkg/wayland" + "github.com/go-gl/gl/v4.1-core/gl" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func (a *App) Draw(window *wayland.WaylandWindow) { + gl.Clear(gl.COLOR_BUFFER_BIT) + + currentTime := float32(time.Since(a.app.StartTime).Seconds()) + + a.drawBackground(currentTime, window) + + x, y := window.GetCursorPos() + a.drawCursorGlow(window, float32(x), float32(y), currentTime) + + for pass := range 3 { + thickness := float32(7 + pass*4) + alpha := float32(0.7 - float32(pass)*0.15) + a.drawLine(window, thickness, alpha, currentTime) + } + + a.drawParticles(window) +} + +func (a *App) drawLine( + window *wayland.WaylandWindow, + baseThickness, baseAlpha, currentTime float32, +) { + if len(a.app.Points) < 2 { + return + } + + vertices := make([]float32, 0, len(a.app.Points)*10) + + for i := range a.app.Points { + age := float32(time.Since(a.app.Points[i].BornTime).Seconds()) + fade := 1.0 - (age / 1.5) + if fade < 0 { + fade = 0 + } + alpha := fade * baseAlpha + + var perpX, perpY float32 + + if i == 0 { + dx := a.app.Points[i+1].X - a.app.Points[i].X + dy := a.app.Points[i+1].Y - a.app.Points[i].Y + length := float32(1.0) / float32(math.Sqrt(float64(dx*dx+dy*dy))) + perpX = -dy * length + perpY = dx * length + } else if i == len(a.app.Points)-1 { + dx := a.app.Points[i].X - a.app.Points[i-1].X + dy := a.app.Points[i].Y - a.app.Points[i-1].Y + length := float32(1.0) / float32(math.Sqrt(float64(dx*dx+dy*dy))) + perpX = -dy * length + perpY = dx * length + } else { + dx1 := a.app.Points[i].X - a.app.Points[i-1].X + dy1 := a.app.Points[i].Y - a.app.Points[i-1].Y + len1 := float32(math.Sqrt(float64(dx1*dx1 + dy1*dy1))) + if len1 > 0 { + dx1 /= len1 + dy1 /= len1 + } + + dx2 := a.app.Points[i+1].X - a.app.Points[i].X + dy2 := a.app.Points[i+1].Y - a.app.Points[i].Y + len2 := float32(math.Sqrt(float64(dx2*dx2 + dy2*dy2))) + if len2 > 0 { + dx2 /= len2 + dy2 /= len2 + } + + avgDx := (dx1 + dx2) * 0.5 + avgDy := (dy1 + dy2) * 0.5 + avgLen := float32(math.Sqrt(float64(avgDx*avgDx + avgDy*avgDy))) + if avgLen > 0 { + avgDx /= avgLen + avgDy /= avgLen + } + + perpX = -avgDy + perpY = avgDx + } + + vertices = append(vertices, a.app.Points[i].X, a.app.Points[i].Y, perpX, perpY, alpha) + vertices = append(vertices, a.app.Points[i].X, a.app.Points[i].Y, -perpX, -perpY, alpha) + } + + cutoff := time.Now().Add(-1500 * time.Millisecond) + for len(a.app.Points) > 0 && a.app.Points[0].BornTime.Before(cutoff) { + a.app.Points = a.app.Points[1:] + } + + if len(vertices) == 0 { + return + } + + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.Vbo) + gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.DYNAMIC_DRAW) + + width, height := window.GetSize() + + gl.UseProgram(a.app.Program) + resolutionLoc := gl.GetUniformLocation(a.app.Program, gl.Str("resolution\x00")) + gl.Uniform2f(resolutionLoc, float32(width), float32(height)) + thicknessLoc := gl.GetUniformLocation(a.app.Program, gl.Str("thickness\x00")) + gl.Uniform1f(thicknessLoc, baseThickness) + timeLoc := gl.GetUniformLocation(a.app.Program, gl.Str("time\x00")) + gl.Uniform1f(timeLoc, currentTime) + + gl.BindVertexArray(a.app.Vao) + gl.DrawArrays(gl.TRIANGLE_STRIP, 0, int32(len(a.app.Points)*2)) + gl.BindVertexArray(0) +} + +func (a *App) drawParticles(window *wayland.WaylandWindow) { + if len(a.app.Particles) == 0 { + return + } + + vertices := make([]float32, 0, len(a.app.Particles)*6) + for _, p := range a.app.Particles { + vertices = append(vertices, p.X, p.Y, p.Life, p.MaxLife, p.Size, p.Hue) + } + + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.ParticleVBO) + gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.DYNAMIC_DRAW) + + width, height := window.GetSize() + + gl.UseProgram(a.app.ParticleProgram) + resolutionLoc := gl.GetUniformLocation(a.app.ParticleProgram, gl.Str("resolution\x00")) + gl.Uniform2f(resolutionLoc, float32(width), float32(height)) + + gl.BindVertexArray(a.app.ParticleVAO) + gl.DrawArrays(gl.POINTS, 0, int32(len(a.app.Particles))) + gl.BindVertexArray(0) +} + +func (a *App) drawBackground(currentTime float32, window *wayland.WaylandWindow) { + fadeDuration := float32(1.0) + targetAlpha := float32(0.75) + + var alpha float32 + if currentTime < fadeDuration { + progress := currentTime / fadeDuration + easedProgress := 1.0 - (1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress) + alpha = easedProgress * targetAlpha + } else { + alpha = targetAlpha + } + + if a.app.IsExiting { + exitDuration := float32(0.8) + elapsed := float32(time.Since(a.app.ExitStartTime).Seconds()) + if elapsed < exitDuration { + progress := elapsed / exitDuration + easedProgress := 1.0 - (1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress) + alpha *= (1.0 - easedProgress) + } else { + alpha = 0 + } + } + + x, y := window.GetCursorPos() + width, height := window.GetSize() + + gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + + gl.UseProgram(a.app.BgProgram) + + alphaLoc := gl.GetUniformLocation(a.app.BgProgram, gl.Str("alpha\x00")) + gl.Uniform1f(alphaLoc, alpha) + + cursorPosLoc := gl.GetUniformLocation(a.app.BgProgram, gl.Str("cursorPos\x00")) + gl.Uniform2f(cursorPosLoc, float32(x), float32(float64(height)-y)) + + resolutionLoc := gl.GetUniformLocation(a.app.BgProgram, gl.Str("resolution\x00")) + gl.Uniform2f(resolutionLoc, float32(width), float32(height)) + + gl.BindVertexArray(a.app.BgVAO) + gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) + gl.BindVertexArray(0) + + gl.BlendFunc(gl.SRC_ALPHA, gl.ONE) +} + +func (a *App) drawCursorGlow(window *wayland.WaylandWindow, cursorX, cursorY, currentTime float32) { + width, height := window.GetSize() + + growDuration := float32(1.2) + var scale float32 + if currentTime < growDuration { + t := currentTime / growDuration + c4 := (2.0 * math.Pi) / 3.0 + if t == 0 { + scale = 0 + } else if t >= 1 { + scale = 1 + } else { + scale = float32(math.Pow(2, -10*float64(t))*math.Sin((float64(t)*10-0.75)*c4) + 1) + } + } else { + scale = 1.0 + } + + var exitProgress float32 + if a.app.IsExiting { + exitDuration := float32(0.8) + elapsed := float32(time.Since(a.app.ExitStartTime).Seconds()) + if elapsed < exitDuration { + t := elapsed / exitDuration + exitProgress = t * t * t + scale *= (1.0 - exitProgress) + } else { + exitProgress = 1.0 + scale = 0 + } + } + + gl.UseProgram(a.app.CursorGlowProgram) + + cursorPosLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("cursorPos\x00")) + gl.Uniform2f(cursorPosLoc, cursorX, cursorY) + + resolutionLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("resolution\x00")) + gl.Uniform2f(resolutionLoc, float32(width), float32(height)) + + glowSizeLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("glowSize\x00")) + gl.Uniform1f(glowSizeLoc, 80.0*scale) + + timeLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("time\x00")) + gl.Uniform1f(timeLoc, currentTime) + + velocityLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("velocity\x00")) + gl.Uniform1f(velocityLoc, a.app.SmoothVelocity) + + rotationLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("rotation\x00")) + gl.Uniform1f(rotationLoc, a.app.SmoothRotation) + + isDrawingLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("isDrawing\x00")) + gl.Uniform1f(isDrawingLoc, a.app.SmoothDrawing) + + exitProgressLoc := gl.GetUniformLocation(a.app.CursorGlowProgram, gl.Str("exitProgress\x00")) + gl.Uniform1f(exitProgressLoc, exitProgress) + + gl.BindVertexArray(a.app.CursorGlowVAO) + gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) + gl.BindVertexArray(0) +} diff --git a/internal/execute/execute.go b/internal/execute/execute.go new file mode 100644 index 0000000..2784e57 --- /dev/null +++ b/internal/execute/execute.go @@ -0,0 +1,78 @@ +package execute + +import ( + "log" + "os/exec" + "syscall" + "time" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" + "github.com/ThatOtherAndrew/Hexecute/internal/spawn" + "github.com/ThatOtherAndrew/Hexecute/internal/stroke" + "github.com/ThatOtherAndrew/Hexecute/pkg/wayland" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func Command(command string) error { + if command == "" { + return nil + } + + cmd := exec.Command("sh", "-c", command) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setsid: true, + } + cmd.Stdin = nil + cmd.Stdout = nil + cmd.Stderr = nil + + return cmd.Start() +} + +func (a *App) RecognizeAndExecute(window *wayland.WaylandWindow, x, y float32) { + if len(a.app.Points) < 5 { + log.Println("Gesture too short, ignoring") + return + } + + processed := stroke.ProcessStroke(a.app.Points) + + bestMatch := -1 + bestScore := 0.0 + + for i, gesture := range a.app.SavedGestures { + match, score := stroke.UnistrokeRecognise(processed, gesture.Templates) + log.Printf("Gesture %d (%s): template %d, score %.3f", i, gesture.Command, match, score) + + if score > bestScore { + bestScore = score + bestMatch = i + } + } + + if bestMatch >= 0 && bestScore > 0.6 { + command := a.app.SavedGestures[bestMatch].Command + log.Printf("Matched gesture: %s (score: %.3f)", command, bestScore) + + if err := Command(command); err != nil { + log.Printf("Failed to execute command: %v", err) + } else { + log.Printf("Executed: %s", command) + } + + a.app.IsExiting = true + a.app.ExitStartTime = time.Now() + window.DisableInput() + spawn := spawn.New(a.app) + spawn.SpawnExitWisps(x, y) + } else { + log.Printf("No confident match (best score: %.3f)", bestScore) + } +} diff --git a/internal/gesture/gesture.go b/internal/gesture/gesture.go new file mode 100644 index 0000000..d0086c1 --- /dev/null +++ b/internal/gesture/gesture.go @@ -0,0 +1,120 @@ +package gestures + +import ( + "encoding/json" + "math" + "math/rand/v2" + "os" + "time" + + "github.com/ThatOtherAndrew/Hexecute/internal/config" + "github.com/ThatOtherAndrew/Hexecute/internal/models" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func LoadGestures() ([]models.GestureConfig, error) { + configFile, err := config.GetPath() + if err != nil { + return nil, err + } + + data, err := os.ReadFile(configFile) + if err != nil { + if os.IsNotExist(err) { + return []models.GestureConfig{}, nil + } + return nil, err + } + + var gestures []models.GestureConfig + if err := json.Unmarshal(data, &gestures); err != nil { + return nil, err + } + + return gestures, nil +} + +func SaveGesture(command string, templates [][]models.Point) error { + configFile, err := config.GetPath() + if err != nil { + return err + } + + var gestures []models.GestureConfig + if data, err := os.ReadFile(configFile); err == nil { + if err := json.Unmarshal(data, &gestures); err != nil { + return err + } + } + + newGesture := models.GestureConfig{ + Command: command, + Templates: templates, + } + + found := false + for i, g := range gestures { + if g.Command == command { + gestures[i] = newGesture + found = true + break + } + } + if !found { + gestures = append(gestures, newGesture) + } + + data, err := json.Marshal(gestures) + if err != nil { + return err + } + + return os.WriteFile(configFile, data, 0644) +} + +func (a *App) AddPoint(x, y float32) { + newPoint := models.Point{X: x, Y: y, BornTime: time.Now()} + + shouldAdd := false + if len(a.app.Points) == 0 { + shouldAdd = true + } else { + lastPoint := a.app.Points[len(a.app.Points)-1] + dx := newPoint.X - lastPoint.X + dy := newPoint.Y - lastPoint.Y + if dx*dx+dy*dy > 4 { + shouldAdd = true + + for range 3 { + angle := rand.Float64() * 2 * math.Pi + speed := rand.Float32()*50 + 20 + a.app.Particles = append(a.app.Particles, models.Particle{ + X: x + (rand.Float32()-0.5)*10, + Y: y + (rand.Float32()-0.5)*10, + VX: float32(math.Cos(angle)) * speed, + VY: float32(math.Sin(angle)) * speed, + Life: 1.0, + MaxLife: 1.0, + Size: rand.Float32()*15 + 10, + Hue: rand.Float32(), + }) + } + } + } + + const MAX_POINTS = 2048 + + if shouldAdd { + a.app.Points = append(a.app.Points, newPoint) + if len(a.app.Points) > MAX_POINTS { + a.app.Points = a.app.Points[len(a.app.Points)-MAX_POINTS:] + } + } +} diff --git a/internal/models/models.go b/internal/models/models.go new file mode 100644 index 0000000..281ad51 --- /dev/null +++ b/internal/models/models.go @@ -0,0 +1,56 @@ +package models + +import ( + "time" +) + +type Point struct { + X, Y float32 + BornTime time.Time `json:"-"` +} + +type Particle struct { + X, Y float32 + VX, VY float32 + Life float32 + MaxLife float32 + Size float32 + Hue float32 +} + +type GestureConfig struct { + Command string `json:"command"` + Templates [][]Point `json:"templates"` +} + +type App struct { + Points []Point + Particles []Particle + IsDrawing bool + Vao uint32 + Vbo uint32 + Program uint32 + ParticleVAO uint32 + ParticleVBO uint32 + ParticleProgram uint32 + BgVAO uint32 + BgVBO uint32 + BgProgram uint32 + CursorGlowVAO uint32 + CursorGlowVBO uint32 + CursorGlowProgram uint32 + StartTime time.Time + LastCursorX float32 + LastCursorY float32 + CursorVelocity float32 + SmoothVelocity float32 + SmoothRotation float32 + SmoothDrawing float32 + IsExiting bool + ExitStartTime time.Time + LearnMode bool + LearnCommand string + LearnGestures [][]Point + LearnCount int + SavedGestures []GestureConfig +} diff --git a/internal/opengl/opengl.go b/internal/opengl/opengl.go new file mode 100644 index 0000000..af390f2 --- /dev/null +++ b/internal/opengl/opengl.go @@ -0,0 +1,229 @@ +package opengl + +import ( + "log" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" + "github.com/ThatOtherAndrew/Hexecute/internal/shaders" + "github.com/go-gl/gl/v4.1-core/gl" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func (a *App) InitGL() error { + if err := gl.Init(); err != nil { + return err + } + + vertShader, err := shaders.CompileShaderFromSource(shaders.LineVertex, gl.VERTEX_SHADER) + if err != nil { + return err + } + fragShader, err := shaders.CompileShaderFromSource(shaders.LineFragment, gl.FRAGMENT_SHADER) + if err != nil { + return err + } + + a.app.Program = gl.CreateProgram() + gl.AttachShader(a.app.Program, vertShader) + gl.AttachShader(a.app.Program, fragShader) + gl.LinkProgram(a.app.Program) + + var status int32 + gl.GetProgramiv(a.app.Program, gl.LINK_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(a.app.Program, gl.INFO_LOG_LENGTH, &logLength) + logMsg := make([]byte, logLength) + gl.GetProgramInfoLog(a.app.Program, logLength, nil, &logMsg[0]) + log.Fatalf("Failed to link program: %s", logMsg) + } + + gl.DeleteShader(vertShader) + gl.DeleteShader(fragShader) + + particleVertShader, err := shaders.CompileShaderFromSource( + shaders.ParticleVertex, + gl.VERTEX_SHADER, + ) + if err != nil { + return err + } + particleFragShader, err := shaders.CompileShaderFromSource( + shaders.ParticleFragment, + gl.FRAGMENT_SHADER, + ) + if err != nil { + return err + } + + a.app.ParticleProgram = gl.CreateProgram() + gl.AttachShader(a.app.ParticleProgram, particleVertShader) + gl.AttachShader(a.app.ParticleProgram, particleFragShader) + gl.LinkProgram(a.app.ParticleProgram) + + gl.GetProgramiv(a.app.ParticleProgram, gl.LINK_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(a.app.ParticleProgram, gl.INFO_LOG_LENGTH, &logLength) + logMsg := make([]byte, logLength) + gl.GetProgramInfoLog(a.app.ParticleProgram, logLength, nil, &logMsg[0]) + log.Fatalf("Failed to link particle program: %s", logMsg) + } + + gl.DeleteShader(particleVertShader) + gl.DeleteShader(particleFragShader) + + gl.GenVertexArrays(1, &a.app.Vao) + gl.GenBuffers(1, &a.app.Vbo) + + gl.BindVertexArray(a.app.Vao) + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.Vbo) + + gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 5*4, nil) + gl.EnableVertexAttribArray(0) + gl.VertexAttribPointerWithOffset(1, 2, gl.FLOAT, false, 5*4, 2*4) + gl.EnableVertexAttribArray(1) + gl.VertexAttribPointerWithOffset(2, 1, gl.FLOAT, false, 5*4, 4*4) + gl.EnableVertexAttribArray(2) + + gl.BindVertexArray(0) + + gl.GenVertexArrays(1, &a.app.ParticleVAO) + gl.GenBuffers(1, &a.app.ParticleVBO) + + gl.BindVertexArray(a.app.ParticleVAO) + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.ParticleVBO) + + gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 6*4, nil) + gl.EnableVertexAttribArray(0) + gl.VertexAttribPointerWithOffset(1, 1, gl.FLOAT, false, 6*4, 2*4) + gl.EnableVertexAttribArray(1) + gl.VertexAttribPointerWithOffset(2, 1, gl.FLOAT, false, 6*4, 3*4) + gl.EnableVertexAttribArray(2) + gl.VertexAttribPointerWithOffset(3, 1, gl.FLOAT, false, 6*4, 4*4) + gl.EnableVertexAttribArray(3) + gl.VertexAttribPointerWithOffset(4, 1, gl.FLOAT, false, 6*4, 5*4) + gl.EnableVertexAttribArray(4) + + gl.BindVertexArray(0) + + bgVertShader, err := shaders.CompileShaderFromSource( + shaders.BackgroundVertex, + gl.VERTEX_SHADER, + ) + if err != nil { + return err + } + bgFragShader, err := shaders.CompileShaderFromSource( + shaders.BackgroundFragment, + gl.FRAGMENT_SHADER, + ) + if err != nil { + return err + } + + a.app.BgProgram = gl.CreateProgram() + gl.AttachShader(a.app.BgProgram, bgVertShader) + gl.AttachShader(a.app.BgProgram, bgFragShader) + gl.LinkProgram(a.app.BgProgram) + + gl.GetProgramiv(a.app.BgProgram, gl.LINK_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(a.app.BgProgram, gl.INFO_LOG_LENGTH, &logLength) + logMsg := make([]byte, logLength) + gl.GetProgramInfoLog(a.app.BgProgram, logLength, nil, &logMsg[0]) + log.Fatalf("Failed to link background program: %s", logMsg) + } + + gl.DeleteShader(bgVertShader) + gl.DeleteShader(bgFragShader) + + gl.GenVertexArrays(1, &a.app.BgVAO) + gl.GenBuffers(1, &a.app.BgVBO) + + gl.BindVertexArray(a.app.BgVAO) + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.BgVBO) + + quadVertices := []float32{ + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0, + } + gl.BufferData(gl.ARRAY_BUFFER, len(quadVertices)*4, gl.Ptr(quadVertices), gl.STATIC_DRAW) + + gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 2*4, nil) + gl.EnableVertexAttribArray(0) + + gl.BindVertexArray(0) + + cursorGlowVertShader, err := shaders.CompileShaderFromSource( + shaders.CursorGlowVertex, + gl.VERTEX_SHADER, + ) + if err != nil { + return err + } + cursorGlowFragShader, err := shaders.CompileShaderFromSource( + shaders.CursorGlowFragment, + gl.FRAGMENT_SHADER, + ) + if err != nil { + return err + } + + a.app.CursorGlowProgram = gl.CreateProgram() + gl.AttachShader(a.app.CursorGlowProgram, cursorGlowVertShader) + gl.AttachShader(a.app.CursorGlowProgram, cursorGlowFragShader) + gl.LinkProgram(a.app.CursorGlowProgram) + + gl.GetProgramiv(a.app.CursorGlowProgram, gl.LINK_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(a.app.CursorGlowProgram, gl.INFO_LOG_LENGTH, &logLength) + logMsg := make([]byte, logLength) + gl.GetProgramInfoLog(a.app.CursorGlowProgram, logLength, nil, &logMsg[0]) + log.Fatalf("Failed to link cursor glow program: %s", logMsg) + } + + gl.DeleteShader(cursorGlowVertShader) + gl.DeleteShader(cursorGlowFragShader) + + gl.GenVertexArrays(1, &a.app.CursorGlowVAO) + gl.GenBuffers(1, &a.app.CursorGlowVBO) + + gl.BindVertexArray(a.app.CursorGlowVAO) + gl.BindBuffer(gl.ARRAY_BUFFER, a.app.CursorGlowVBO) + + glowQuadVertices := []float32{ + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0, + } + gl.BufferData( + gl.ARRAY_BUFFER, + len(glowQuadVertices)*4, + gl.Ptr(glowQuadVertices), + gl.STATIC_DRAW, + ) + + gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 2*4, nil) + gl.EnableVertexAttribArray(0) + + gl.BindVertexArray(0) + + gl.Enable(gl.BLEND) + gl.BlendFunc(gl.SRC_ALPHA, gl.ONE) + gl.Enable(gl.PROGRAM_POINT_SIZE) + + return nil +} diff --git a/internal/shaders/background.frag.glsl b/internal/shaders/background.frag.glsl new file mode 100644 index 0000000..2f4dc31 --- /dev/null +++ b/internal/shaders/background.frag.glsl @@ -0,0 +1,15 @@ +#version 410 core +out vec4 FragColor; + +uniform float alpha; +uniform vec2 cursorPos; +uniform vec2 resolution; + +void main() { + vec2 fragCoord = gl_FragCoord.xy; + float dist = length(fragCoord - cursorPos); + float glowFalloff = smoothstep(0.0, 300.0, dist); + float cursorTransparency = mix(0.3, 1.0, glowFalloff); + + FragColor = vec4(0., 0., 0., alpha * cursorTransparency); +} diff --git a/internal/shaders/background.vert.glsl b/internal/shaders/background.vert.glsl new file mode 100644 index 0000000..a48563d --- /dev/null +++ b/internal/shaders/background.vert.glsl @@ -0,0 +1,6 @@ +#version 410 core +layout (location = 0) in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} diff --git a/internal/shaders/cursorGlow.frag.glsl b/internal/shaders/cursorGlow.frag.glsl new file mode 100644 index 0000000..edfb78a --- /dev/null +++ b/internal/shaders/cursorGlow.frag.glsl @@ -0,0 +1,129 @@ +#version 410 core +in vec2 vTexCoord; +out vec4 FragColor; + +uniform float time; +uniform float velocity; +uniform float isDrawing; +uniform float exitProgress; + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +float smin(float a, float b, float k) { + float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); + return mix(b, a, h) - k * h * (1.0 - h); +} + +float hash(vec2 p) { + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + + float a = hash(i); + float b = hash(i + vec2(1.0, 0.0)); + float c = hash(i + vec2(0.0, 1.0)); + float d = hash(i + vec2(1.0, 1.0)); + + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + + for(int i = 0; i < 4; i++) { + value += amplitude * noise(p * frequency); + frequency *= 2.0; + amplitude *= 0.5; + } + return value; +} + +void main() { + vec2 coord = vTexCoord * 2.0 - 1.0; + float velocityNorm = clamp(velocity * 0.01, 0.0, 1.0); + float energy = mix(0.3, 1.0, velocityNorm) + isDrawing * 0.7; + + float sdf = 1000.0; + float centralSize = mix(0.15, 0.35, velocityNorm) + isDrawing * 0.05; + float pulseSpeed = (3.0 + velocityNorm * 2.0) * (1.0 + isDrawing * 0.75); + float pulseAmount = (0.1 * energy + isDrawing * 0.075); + float pulse = sin(time * pulseSpeed) * pulseAmount + 0.9; + float centralDist = length(coord) - centralSize * pulse; + sdf = centralDist; + + float numBlobsFloat = mix(5.0, 9.0, velocityNorm) + isDrawing * 1.0; + int numBlobs = int(numBlobsFloat); + float blobFade = fract(numBlobsFloat); + + for(int i = 0; i < 10; i++) { + if(i > numBlobs) break; + if(i == numBlobs && blobFade < 0.01) break; + float baseRotation = time * 0.8; + float velocityRotation = time * velocityNorm * 0.4; + float angle = float(i) * 6.28 / float(numBlobs) + baseRotation + velocityRotation; + + float baseRadius = mix(0.2, 0.5, velocityNorm) + isDrawing * 0.075; + float radiusVariation = sin(time * (1.5 + isDrawing * 0.5) + float(i) * 0.8) * mix(0.05, 0.15, velocityNorm); + float chaoticRadius = sin(time * 4.0 + float(i) * 2.1) * cos(time * 3.5 + float(i) * 1.7) * 0.003 * isDrawing; + float radius = baseRadius + radiusVariation + chaoticRadius; + vec2 orbPos = vec2(cos(angle), sin(angle)) * radius; + + float baseBlobSize = mix(0.08, 0.18, velocityNorm) + isDrawing * 0.04; + float sizeVariation = sin(time * (2.5 + isDrawing * 1.0) + float(i) * 0.6) * mix(0.02, 0.05, velocityNorm); + float drawingGrowth = sin(time * 5.0 + float(i) * 1.3) * 0.03 * isDrawing; + float blobSize = baseBlobSize + sizeVariation + drawingGrowth; + float blobDist = length(coord - orbPos) - blobSize; + + if(i == numBlobs) { + blobDist += (1.0 - blobFade) * 0.5; + } + float blendAmount = mix(0.15, 0.3, velocityNorm) + isDrawing * 0.075; + sdf = smin(sdf, blobDist, blendAmount); + } + + float noiseZoom = 3.0 + isDrawing * 0.5; + vec2 noiseCoord = coord * noiseZoom; + noiseCoord += vec2(time * 0.3, time * 0.2); + float swirl = fbm(noiseCoord) * 2.0 - 1.0; + + sdf += swirl * (0.1 * energy + exitProgress * 0.8); + float intensity = exp(-max(sdf, 0.0) * 4.0); + float outerGlow = exp(-max(sdf, 0.0) * 1.5) * 0.4 * energy; + float innerGlow = exp(-max(sdf, 0.0) * 8.0) * 0.8; + + float totalIntensity = intensity + outerGlow + innerGlow; + + totalIntensity *= smoothstep(1.0, 0.7, max(abs(coord.x), abs(coord.y))); + + float hueSpeed = mix(0.2, 0.6, velocityNorm); + float hue = mod(time * hueSpeed + atan(coord.y, coord.x) / 6.28 + swirl * 0.3, 1.0); + vec3 mainColor = hsv2rgb(vec3(hue, mix(0.7, 0.75, velocityNorm), 1.0)); + vec3 accentColor = hsv2rgb(vec3(mod(hue + 0.5, 1.0), 0.75, 1.2)); + vec3 finalColor = mainColor * intensity; + finalColor += accentColor * innerGlow; + finalColor += mainColor * 0.5 * outerGlow; + + float sparkle = smoothstep(0.85, 1.0, noise(coord * 20.0 + time * 5.0 * energy)) * totalIntensity * velocityNorm; + finalColor += sparkle; + + float edge = smoothstep(0.05, -0.05, sdf) - smoothstep(0.15, 0.05, sdf); + finalColor += accentColor * edge * energy; + + finalColor *= sin(time * (2.5 + isDrawing * 0.75)) * (0.1 + velocityNorm * 0.1 + isDrawing * 0.075) + 0.9; + + float alpha = clamp(totalIntensity * mix(0.8, 1.3, velocityNorm), 0.0, 1.0) * (1.0 - exitProgress); + + FragColor = vec4(finalColor, alpha * 0.95); +} diff --git a/internal/shaders/cursorGlow.vert.glsl b/internal/shaders/cursorGlow.vert.glsl new file mode 100644 index 0000000..ac68929 --- /dev/null +++ b/internal/shaders/cursorGlow.vert.glsl @@ -0,0 +1,20 @@ +#version 410 core +layout (location = 0) in vec2 position; + +uniform vec2 cursorPos; +uniform vec2 resolution; +uniform float glowSize; +uniform float rotation; + +out vec2 vTexCoord; + +void main() { + float c = cos(rotation); + float s = sin(rotation); + vec2 rotatedPos = vec2(position.x * c - position.y * s, position.x * s + position.y * c); + vec2 worldPos = cursorPos + rotatedPos * glowSize; + vec2 normalized = (worldPos / resolution) * 2.0 - 1.0; + normalized.y = -normalized.y; + gl_Position = vec4(normalized, 0.0, 1.0); + vTexCoord = rotatedPos * 0.5 + 0.5; +} diff --git a/internal/shaders/line.frag.glsl b/internal/shaders/line.frag.glsl new file mode 100644 index 0000000..e0ce5a0 --- /dev/null +++ b/internal/shaders/line.frag.glsl @@ -0,0 +1,22 @@ +#version 410 core +in float vAlpha; +in vec2 vPosition; +out vec4 FragColor; + +uniform float time; + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() { + float hue = mod(vPosition.x * 0.001 + vPosition.y * 0.001 + time * 0.5, 1.0); + vec3 color = hsv2rgb(vec3(hue, 0.8, 1.0)); + + float sparkle = sin(vPosition.x * 0.1 + time * 3.0) * sin(vPosition.y * 0.1 + time * 2.0); + sparkle = smoothstep(0.7, 1.0, sparkle) * 0.5; + + FragColor = vec4(color * (1.0 + sparkle * 2.0), vAlpha); +} diff --git a/internal/shaders/line.vert.glsl b/internal/shaders/line.vert.glsl new file mode 100644 index 0000000..62cb8e0 --- /dev/null +++ b/internal/shaders/line.vert.glsl @@ -0,0 +1,19 @@ +#version 410 core +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 offset; +layout (location = 2) in float alpha; + +uniform vec2 resolution; +uniform float thickness; + +out float vAlpha; +out vec2 vPosition; + +void main() { + vec2 pos = position + offset * thickness; + vec2 normalized = (pos / resolution) * 2.0 - 1.0; + normalized.y = -normalized.y; + gl_Position = vec4(normalized, 0.0, 1.0); + vAlpha = alpha; + vPosition = pos; +} diff --git a/internal/shaders/particle.frag.glsl b/internal/shaders/particle.frag.glsl new file mode 100644 index 0000000..aef6f60 --- /dev/null +++ b/internal/shaders/particle.frag.glsl @@ -0,0 +1,21 @@ +#version 410 core +in float vLife; +in float vHue; +out vec4 FragColor; + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() { + vec2 coord = gl_PointCoord - vec2(0.5); + float dist = length(coord); + if (dist > 0.5) discard; + + float alpha = smoothstep(0.5, 0.2, dist) * vLife; + vec3 color = hsv2rgb(vec3(vHue, 0.9, 1.0)) * (1.0 + (1.0 - dist * 2.0) * 2.0); + + FragColor = vec4(color, alpha * 0.8); +} diff --git a/internal/shaders/particle.vert.glsl b/internal/shaders/particle.vert.glsl new file mode 100644 index 0000000..3d165aa --- /dev/null +++ b/internal/shaders/particle.vert.glsl @@ -0,0 +1,20 @@ +#version 410 core +layout (location = 0) in vec2 position; +layout (location = 1) in float life; +layout (location = 2) in float maxLife; +layout (location = 3) in float size; +layout (location = 4) in float hue; + +uniform vec2 resolution; + +out float vLife; +out float vHue; + +void main() { + vec2 normalized = (position / resolution) * 2.0 - 1.0; + normalized.y = -normalized.y; + gl_Position = vec4(normalized, 0.0, 1.0); + gl_PointSize = size * (life / maxLife); + vLife = life / maxLife; + vHue = hue; +} diff --git a/internal/shaders/paths.go b/internal/shaders/paths.go new file mode 100644 index 0000000..36122ff --- /dev/null +++ b/internal/shaders/paths.go @@ -0,0 +1,41 @@ +package shaders + +import _ "embed" + +// TODO: select one to choose embed the shaders or place like system files. +const BackgroundFragmentPath = "internal/shaders/background.frag.glsl" +const BackgroundVertexPath = "internal/shaders/background.vert.glsl" +const CursorGlowFragmentPath = "internal/shaders/cursorGlow.frag.glsl" +const CursorGlowVertexPath = "internal/shaders/cursorGlow.vert.glsl" +const LineFragmentPath = "internal/shaders/line.frag.glsl" +const LineVertexPath = "internal/shaders/line.vert.glsl" +const ParticleVertexPath = "internal/shaders/particle.vert.glsl" +const ParticleFragmentPath = "internal/shaders/particle.frag.glsl" + +// Vertex shaders +// +//go:embed background.vert.glsl +var BackgroundVertex string + +//go:embed cursorGlow.vert.glsl +var CursorGlowVertex string + +//go:embed line.vert.glsl +var LineVertex string + +//go:embed particle.vert.glsl +var ParticleVertex string + +// Fragment shaders +// +//go:embed background.frag.glsl +var BackgroundFragment string + +//go:embed cursorGlow.frag.glsl +var CursorGlowFragment string + +//go:embed line.frag.glsl +var LineFragment string + +//go:embed particle.frag.glsl +var ParticleFragment string diff --git a/internal/shaders/shader.go b/internal/shaders/shader.go new file mode 100644 index 0000000..6237f04 --- /dev/null +++ b/internal/shaders/shader.go @@ -0,0 +1,65 @@ +package shaders + +import ( + "fmt" + "os" + "strings" + + "github.com/go-gl/gl/v4.1-core/gl" +) + +// TODO: select either one or use both +func CompileShaderFromFile(path string, shaderType uint32) (uint32, error) { + sourceBytes, err := os.ReadFile(path) + if err != nil { + return 0, fmt.Errorf("failed to read shader file %q: %v", path, err) + } + + source := string(sourceBytes) + + if !strings.HasSuffix(source, "\x00") { + source += "\x00" + } + + shader := gl.CreateShader(shaderType) + csources, free := gl.Strs(source) + defer free() + + gl.ShaderSource(shader, 1, csources, nil) + gl.CompileShader(shader) + + var status int32 + gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) + + logMsg := strings.Repeat("\x00", int(logLength+1)) + gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(logMsg)) + + gl.DeleteShader(shader) + return 0, fmt.Errorf("failed to compile %s shader: %v", path, strings.TrimSpace(logMsg)) + } + + return shader, nil +} + +func CompileShaderFromSource(source string, shaderType uint32) (uint32, error) { + shader := gl.CreateShader(shaderType) + csources, free := gl.Strs(source + "\x00") + gl.ShaderSource(shader, 1, csources, nil) + free() + gl.CompileShader(shader) + + var status int32 + gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) + logMsg := make([]byte, logLength) + gl.GetShaderInfoLog(shader, logLength, nil, &logMsg[0]) + return 0, fmt.Errorf("failed to compile shader: %s", logMsg) + } + + return shader, nil +} diff --git a/internal/spawn/spawn.go b/internal/spawn/spawn.go new file mode 100644 index 0000000..5806d6b --- /dev/null +++ b/internal/spawn/spawn.go @@ -0,0 +1,50 @@ +package spawn + +import ( + "math" + "math/rand/v2" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func (a *App) SpawnCursorSparkles(x, y float32) { + for range 3 { + angle := rand.Float64() * 2 * math.Pi + speed := rand.Float32()*80 + 40 + a.app.Particles = append(a.app.Particles, models.Particle{ + X: x + (rand.Float32()-0.5)*8, + Y: y + (rand.Float32()-0.5)*8, + VX: float32(math.Cos(angle)) * speed, + VY: float32(math.Sin(angle))*speed - 30, + Life: 0.8, + MaxLife: 0.8, + Size: rand.Float32()*8 + 6, + Hue: rand.Float32(), + }) + } +} + +func (a *App) SpawnExitWisps(x, y float32) { + for range 8 { + angle := rand.Float64() * 2 * math.Pi + speed := rand.Float32()*150 + 80 + a.app.Particles = append(a.app.Particles, models.Particle{ + X: x + (rand.Float32()-0.5)*30, + Y: y + (rand.Float32()-0.5)*30, + VX: float32(math.Cos(angle)) * speed, + VY: float32(math.Sin(angle)) * speed, + Life: 1.2, + MaxLife: 1.2, + Size: rand.Float32()*12 + 8, + Hue: rand.Float32(), + }) + } +} diff --git a/stroke.go b/internal/stroke/stroke.go similarity index 95% rename from stroke.go rename to internal/stroke/stroke.go index 2dc77b8..730b186 100644 --- a/stroke.go +++ b/internal/stroke/stroke.go @@ -1,11 +1,17 @@ // https://depts.washington.edu/acelab/proj/dollar/dollar.pdf -package main +package stroke -import "math" +import ( + "math" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" +) // Step 1 +type Point = models.Point + func resample(points []Point, n int) []Point { I := pathLength(points) / float32(n-1) D := float32(0) @@ -110,7 +116,11 @@ func centroid(points []Point) Point { // Step 4 -func recognise(points []Point, templates [][]Point, size float64) (bestMatch int, bestScore float64) { +func recognise( + points []Point, + templates [][]Point, + size float64, +) (bestMatch int, bestScore float64) { b := math.Inf(1) for i, T := range templates { d := distanceAtBestAngle(points, T, -math.Pi/4, math.Pi/4, math.Pi/90) diff --git a/internal/update/update.go b/internal/update/update.go new file mode 100644 index 0000000..4b0a140 --- /dev/null +++ b/internal/update/update.go @@ -0,0 +1,72 @@ +package update + +import ( + "math" + + "github.com/ThatOtherAndrew/Hexecute/internal/models" + "github.com/ThatOtherAndrew/Hexecute/pkg/wayland" +) + +type App struct { + app *models.App +} + +func New(app *models.App) *App { + return &App{app: app} +} + +func (a *App) UpdateParticles(dt float32) { + for i := 0; i < len(a.app.Particles); i++ { + p := &a.app.Particles[i] + p.X += p.VX * dt + p.Y += p.VY * dt + p.VY += 100 * dt + p.Life -= dt + + if p.Life <= 0 { + a.app.Particles[i] = a.app.Particles[len(a.app.Particles)-1] + a.app.Particles = a.app.Particles[:len(a.app.Particles)-1] + i-- + } + } +} + +func (a *App) UpdateCursor(window *wayland.WaylandWindow) { + x, y := window.GetCursorPos() + fx, fy := float32(x), float32(y) + + dx := fx - a.app.LastCursorX + dy := fy - a.app.LastCursorY + a.app.CursorVelocity = float32(math.Sqrt(float64(dx*dx + dy*dy))) + + velocityDiff := a.app.CursorVelocity - a.app.SmoothVelocity + a.app.SmoothVelocity += velocityDiff * 0.2 + + if a.app.CursorVelocity > 0.1 { + targetRotation := float32(math.Atan2(float64(dy), float64(dx))) + + angleDiff := targetRotation - a.app.SmoothRotation + if angleDiff > math.Pi { + angleDiff -= 2 * math.Pi + } else if angleDiff < -math.Pi { + angleDiff += 2 * math.Pi + } + + velocityFactor := float32(math.Min(float64(a.app.SmoothVelocity/5.0), 1.0)) + smoothFactor := 0.03 + velocityFactor*0.08 + a.app.SmoothRotation += angleDiff * smoothFactor + } + + var targetDrawing float32 + if a.app.IsDrawing { + targetDrawing = 1.0 + } else { + targetDrawing = 0.0 + } + + diff := targetDrawing - a.app.SmoothDrawing + a.app.SmoothDrawing += diff * 0.0375 + + a.app.LastCursorX = fx + a.app.LastCursorY = fy +} diff --git a/main.go b/main.go deleted file mode 100644 index 2549a25..0000000 --- a/main.go +++ /dev/null @@ -1,1232 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "log" - "math" - "math/rand" - "os" - "os/exec" - "path/filepath" - "runtime" - "syscall" - "time" - - "github.com/go-gl/gl/v4.1-core/gl" -) - -type Point struct { - X, Y float32 - BornTime time.Time `json:"-"` -} - -type Particle struct { - X, Y float32 - VX, VY float32 - Life float32 - MaxLife float32 - Size float32 - Hue float32 -} - -type App struct { - points []Point - particles []Particle - isDrawing bool - vao uint32 - vbo uint32 - program uint32 - particleVAO uint32 - particleVBO uint32 - particleProgram uint32 - bgVAO uint32 - bgVBO uint32 - bgProgram uint32 - cursorGlowVAO uint32 - cursorGlowVBO uint32 - cursorGlowProgram uint32 - startTime time.Time - lastCursorX float32 - lastCursorY float32 - cursorVelocity float32 - smoothVelocity float32 - smoothRotation float32 - smoothDrawing float32 - isExiting bool - exitStartTime time.Time - learnMode bool - learnCommand string - learnGestures [][]Point - learnCount int - savedGestures []GestureConfig -} - -const lineVertexShader = ` -#version 410 core -layout (location = 0) in vec2 position; -layout (location = 1) in vec2 offset; -layout (location = 2) in float alpha; - -uniform vec2 resolution; -uniform float thickness; - -out float vAlpha; -out vec2 vPosition; - -void main() { - vec2 pos = position + offset * thickness; - vec2 normalized = (pos / resolution) * 2.0 - 1.0; - normalized.y = -normalized.y; - gl_Position = vec4(normalized, 0.0, 1.0); - vAlpha = alpha; - vPosition = pos; -} -` + "\x00" - -const lineFragmentShader = ` -#version 410 core -in float vAlpha; -in vec2 vPosition; -out vec4 FragColor; - -uniform float time; - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -void main() { - float hue = mod(vPosition.x * 0.001 + vPosition.y * 0.001 + time * 0.5, 1.0); - vec3 color = hsv2rgb(vec3(hue, 0.8, 1.0)); - - float sparkle = sin(vPosition.x * 0.1 + time * 3.0) * sin(vPosition.y * 0.1 + time * 2.0); - sparkle = smoothstep(0.7, 1.0, sparkle) * 0.5; - - FragColor = vec4(color * (1.0 + sparkle * 2.0), vAlpha); -} -` + "\x00" - -const particleVertexShader = ` -#version 410 core -layout (location = 0) in vec2 position; -layout (location = 1) in float life; -layout (location = 2) in float maxLife; -layout (location = 3) in float size; -layout (location = 4) in float hue; - -uniform vec2 resolution; - -out float vLife; -out float vHue; - -void main() { - vec2 normalized = (position / resolution) * 2.0 - 1.0; - normalized.y = -normalized.y; - gl_Position = vec4(normalized, 0.0, 1.0); - gl_PointSize = size * (life / maxLife); - vLife = life / maxLife; - vHue = hue; -} -` + "\x00" - -const particleFragmentShader = ` -#version 410 core -in float vLife; -in float vHue; -out vec4 FragColor; - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -void main() { - vec2 coord = gl_PointCoord - vec2(0.5); - float dist = length(coord); - if (dist > 0.5) discard; - - float alpha = smoothstep(0.5, 0.2, dist) * vLife; - vec3 color = hsv2rgb(vec3(vHue, 0.9, 1.0)) * (1.0 + (1.0 - dist * 2.0) * 2.0); - - FragColor = vec4(color, alpha * 0.8); -} -` + "\x00" - -const backgroundVertexShader = ` -#version 410 core -layout (location = 0) in vec2 position; - -void main() { - gl_Position = vec4(position, 0.0, 1.0); -} -` + "\x00" - -const backgroundFragmentShader = ` -#version 410 core -out vec4 FragColor; - -uniform float alpha; -uniform vec2 cursorPos; -uniform vec2 resolution; - -void main() { - vec2 fragCoord = gl_FragCoord.xy; - float dist = length(fragCoord - cursorPos); - float glowFalloff = smoothstep(0.0, 300.0, dist); - float cursorTransparency = mix(0.3, 1.0, glowFalloff); - - FragColor = vec4(0., 0., 0., alpha * cursorTransparency); -} -` + "\x00" - -const cursorGlowVertexShader = ` -#version 410 core -layout (location = 0) in vec2 position; - -uniform vec2 cursorPos; -uniform vec2 resolution; -uniform float glowSize; -uniform float rotation; - -out vec2 vTexCoord; - -void main() { - float c = cos(rotation); - float s = sin(rotation); - vec2 rotatedPos = vec2(position.x * c - position.y * s, position.x * s + position.y * c); - vec2 worldPos = cursorPos + rotatedPos * glowSize; - vec2 normalized = (worldPos / resolution) * 2.0 - 1.0; - normalized.y = -normalized.y; - gl_Position = vec4(normalized, 0.0, 1.0); - vTexCoord = rotatedPos * 0.5 + 0.5; -} -` + "\x00" - -const cursorGlowFragmentShader = ` -#version 410 core -in vec2 vTexCoord; -out vec4 FragColor; - -uniform float time; -uniform float velocity; -uniform float isDrawing; -uniform float exitProgress; - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -float smin(float a, float b, float k) { - float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); - return mix(b, a, h) - k * h * (1.0 - h); -} - -float hash(vec2 p) { - p = fract(p * vec2(123.34, 456.21)); - p += dot(p, p + 45.32); - return fract(p.x * p.y); -} - -float noise(vec2 p) { - vec2 i = floor(p); - vec2 f = fract(p); - f = f * f * (3.0 - 2.0 * f); - - float a = hash(i); - float b = hash(i + vec2(1.0, 0.0)); - float c = hash(i + vec2(0.0, 1.0)); - float d = hash(i + vec2(1.0, 1.0)); - - return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); -} - -float fbm(vec2 p) { - float value = 0.0; - float amplitude = 0.5; - float frequency = 1.0; - - for(int i = 0; i < 4; i++) { - value += amplitude * noise(p * frequency); - frequency *= 2.0; - amplitude *= 0.5; - } - return value; -} - -void main() { - vec2 coord = vTexCoord * 2.0 - 1.0; - float velocityNorm = clamp(velocity * 0.01, 0.0, 1.0); - float energy = mix(0.3, 1.0, velocityNorm) + isDrawing * 0.7; - - float sdf = 1000.0; - float centralSize = mix(0.15, 0.35, velocityNorm) + isDrawing * 0.05; - float pulseSpeed = (3.0 + velocityNorm * 2.0) * (1.0 + isDrawing * 0.75); - float pulseAmount = (0.1 * energy + isDrawing * 0.075); - float pulse = sin(time * pulseSpeed) * pulseAmount + 0.9; - float centralDist = length(coord) - centralSize * pulse; - sdf = centralDist; - - float numBlobsFloat = mix(5.0, 9.0, velocityNorm) + isDrawing * 1.0; - int numBlobs = int(numBlobsFloat); - float blobFade = fract(numBlobsFloat); - - for(int i = 0; i < 10; i++) { - if(i > numBlobs) break; - if(i == numBlobs && blobFade < 0.01) break; - float baseRotation = time * 0.8; - float velocityRotation = time * velocityNorm * 0.4; - float angle = float(i) * 6.28 / float(numBlobs) + baseRotation + velocityRotation; - - float baseRadius = mix(0.2, 0.5, velocityNorm) + isDrawing * 0.075; - float radiusVariation = sin(time * (1.5 + isDrawing * 0.5) + float(i) * 0.8) * mix(0.05, 0.15, velocityNorm); - float chaoticRadius = sin(time * 4.0 + float(i) * 2.1) * cos(time * 3.5 + float(i) * 1.7) * 0.003 * isDrawing; - float radius = baseRadius + radiusVariation + chaoticRadius; - vec2 orbPos = vec2(cos(angle), sin(angle)) * radius; - - float baseBlobSize = mix(0.08, 0.18, velocityNorm) + isDrawing * 0.04; - float sizeVariation = sin(time * (2.5 + isDrawing * 1.0) + float(i) * 0.6) * mix(0.02, 0.05, velocityNorm); - float drawingGrowth = sin(time * 5.0 + float(i) * 1.3) * 0.03 * isDrawing; - float blobSize = baseBlobSize + sizeVariation + drawingGrowth; - float blobDist = length(coord - orbPos) - blobSize; - - if(i == numBlobs) { - blobDist += (1.0 - blobFade) * 0.5; - } - float blendAmount = mix(0.15, 0.3, velocityNorm) + isDrawing * 0.075; - sdf = smin(sdf, blobDist, blendAmount); - } - - float noiseZoom = 3.0 + isDrawing * 0.5; - vec2 noiseCoord = coord * noiseZoom; - noiseCoord += vec2(time * 0.3, time * 0.2); - float swirl = fbm(noiseCoord) * 2.0 - 1.0; - - sdf += swirl * (0.1 * energy + exitProgress * 0.8); - float intensity = exp(-max(sdf, 0.0) * 4.0); - float outerGlow = exp(-max(sdf, 0.0) * 1.5) * 0.4 * energy; - float innerGlow = exp(-max(sdf, 0.0) * 8.0) * 0.8; - - float totalIntensity = intensity + outerGlow + innerGlow; - - totalIntensity *= smoothstep(1.0, 0.7, max(abs(coord.x), abs(coord.y))); - - float hueSpeed = mix(0.2, 0.6, velocityNorm); - float hue = mod(time * hueSpeed + atan(coord.y, coord.x) / 6.28 + swirl * 0.3, 1.0); - vec3 mainColor = hsv2rgb(vec3(hue, mix(0.7, 0.75, velocityNorm), 1.0)); - vec3 accentColor = hsv2rgb(vec3(mod(hue + 0.5, 1.0), 0.75, 1.2)); - vec3 finalColor = mainColor * intensity; - finalColor += accentColor * innerGlow; - finalColor += mainColor * 0.5 * outerGlow; - - float sparkle = smoothstep(0.85, 1.0, noise(coord * 20.0 + time * 5.0 * energy)) * totalIntensity * velocityNorm; - finalColor += sparkle; - - float edge = smoothstep(0.05, -0.05, sdf) - smoothstep(0.15, 0.05, sdf); - finalColor += accentColor * edge * energy; - - finalColor *= sin(time * (2.5 + isDrawing * 0.75)) * (0.1 + velocityNorm * 0.1 + isDrawing * 0.075) + 0.9; - - float alpha = clamp(totalIntensity * mix(0.8, 1.3, velocityNorm), 0.0, 1.0) * (1.0 - exitProgress); - - FragColor = vec4(finalColor, alpha * 0.95); -} -` + "\x00" - -func init() { - runtime.LockOSThread() -} - -func (a *App) initGL() error { - if err := gl.Init(); err != nil { - return err - } - - vertShader, err := compileShader(lineVertexShader, gl.VERTEX_SHADER) - if err != nil { - return err - } - fragShader, err := compileShader(lineFragmentShader, gl.FRAGMENT_SHADER) - if err != nil { - return err - } - - a.program = gl.CreateProgram() - gl.AttachShader(a.program, vertShader) - gl.AttachShader(a.program, fragShader) - gl.LinkProgram(a.program) - - var status int32 - gl.GetProgramiv(a.program, gl.LINK_STATUS, &status) - if status == gl.FALSE { - var logLength int32 - gl.GetProgramiv(a.program, gl.INFO_LOG_LENGTH, &logLength) - logMsg := make([]byte, logLength) - gl.GetProgramInfoLog(a.program, logLength, nil, &logMsg[0]) - log.Fatalf("Failed to link program: %s", logMsg) - } - - gl.DeleteShader(vertShader) - gl.DeleteShader(fragShader) - - particleVertShader, err := compileShader(particleVertexShader, gl.VERTEX_SHADER) - if err != nil { - return err - } - particleFragShader, err := compileShader(particleFragmentShader, gl.FRAGMENT_SHADER) - if err != nil { - return err - } - - a.particleProgram = gl.CreateProgram() - gl.AttachShader(a.particleProgram, particleVertShader) - gl.AttachShader(a.particleProgram, particleFragShader) - gl.LinkProgram(a.particleProgram) - - gl.GetProgramiv(a.particleProgram, gl.LINK_STATUS, &status) - if status == gl.FALSE { - var logLength int32 - gl.GetProgramiv(a.particleProgram, gl.INFO_LOG_LENGTH, &logLength) - logMsg := make([]byte, logLength) - gl.GetProgramInfoLog(a.particleProgram, logLength, nil, &logMsg[0]) - log.Fatalf("Failed to link particle program: %s", logMsg) - } - - gl.DeleteShader(particleVertShader) - gl.DeleteShader(particleFragShader) - - gl.GenVertexArrays(1, &a.vao) - gl.GenBuffers(1, &a.vbo) - - gl.BindVertexArray(a.vao) - gl.BindBuffer(gl.ARRAY_BUFFER, a.vbo) - - gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 5*4, nil) - gl.EnableVertexAttribArray(0) - gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(2*4)) - gl.EnableVertexAttribArray(1) - gl.VertexAttribPointer(2, 1, gl.FLOAT, false, 5*4, gl.PtrOffset(4*4)) - gl.EnableVertexAttribArray(2) - - gl.BindVertexArray(0) - - gl.GenVertexArrays(1, &a.particleVAO) - gl.GenBuffers(1, &a.particleVBO) - - gl.BindVertexArray(a.particleVAO) - gl.BindBuffer(gl.ARRAY_BUFFER, a.particleVBO) - - gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 6*4, nil) - gl.EnableVertexAttribArray(0) - gl.VertexAttribPointer(1, 1, gl.FLOAT, false, 6*4, gl.PtrOffset(2*4)) - gl.EnableVertexAttribArray(1) - gl.VertexAttribPointer(2, 1, gl.FLOAT, false, 6*4, gl.PtrOffset(3*4)) - gl.EnableVertexAttribArray(2) - gl.VertexAttribPointer(3, 1, gl.FLOAT, false, 6*4, gl.PtrOffset(4*4)) - gl.EnableVertexAttribArray(3) - gl.VertexAttribPointer(4, 1, gl.FLOAT, false, 6*4, gl.PtrOffset(5*4)) - gl.EnableVertexAttribArray(4) - - gl.BindVertexArray(0) - - bgVertShader, err := compileShader(backgroundVertexShader, gl.VERTEX_SHADER) - if err != nil { - return err - } - bgFragShader, err := compileShader(backgroundFragmentShader, gl.FRAGMENT_SHADER) - if err != nil { - return err - } - - a.bgProgram = gl.CreateProgram() - gl.AttachShader(a.bgProgram, bgVertShader) - gl.AttachShader(a.bgProgram, bgFragShader) - gl.LinkProgram(a.bgProgram) - - gl.GetProgramiv(a.bgProgram, gl.LINK_STATUS, &status) - if status == gl.FALSE { - var logLength int32 - gl.GetProgramiv(a.bgProgram, gl.INFO_LOG_LENGTH, &logLength) - logMsg := make([]byte, logLength) - gl.GetProgramInfoLog(a.bgProgram, logLength, nil, &logMsg[0]) - log.Fatalf("Failed to link background program: %s", logMsg) - } - - gl.DeleteShader(bgVertShader) - gl.DeleteShader(bgFragShader) - - gl.GenVertexArrays(1, &a.bgVAO) - gl.GenBuffers(1, &a.bgVBO) - - gl.BindVertexArray(a.bgVAO) - gl.BindBuffer(gl.ARRAY_BUFFER, a.bgVBO) - - quadVertices := []float32{ - -1.0, -1.0, - 1.0, -1.0, - -1.0, 1.0, - 1.0, 1.0, - } - gl.BufferData(gl.ARRAY_BUFFER, len(quadVertices)*4, gl.Ptr(quadVertices), gl.STATIC_DRAW) - - gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 2*4, nil) - gl.EnableVertexAttribArray(0) - - gl.BindVertexArray(0) - - cursorGlowVertShader, err := compileShader(cursorGlowVertexShader, gl.VERTEX_SHADER) - if err != nil { - return err - } - cursorGlowFragShader, err := compileShader(cursorGlowFragmentShader, gl.FRAGMENT_SHADER) - if err != nil { - return err - } - - a.cursorGlowProgram = gl.CreateProgram() - gl.AttachShader(a.cursorGlowProgram, cursorGlowVertShader) - gl.AttachShader(a.cursorGlowProgram, cursorGlowFragShader) - gl.LinkProgram(a.cursorGlowProgram) - - gl.GetProgramiv(a.cursorGlowProgram, gl.LINK_STATUS, &status) - if status == gl.FALSE { - var logLength int32 - gl.GetProgramiv(a.cursorGlowProgram, gl.INFO_LOG_LENGTH, &logLength) - logMsg := make([]byte, logLength) - gl.GetProgramInfoLog(a.cursorGlowProgram, logLength, nil, &logMsg[0]) - log.Fatalf("Failed to link cursor glow program: %s", logMsg) - } - - gl.DeleteShader(cursorGlowVertShader) - gl.DeleteShader(cursorGlowFragShader) - - gl.GenVertexArrays(1, &a.cursorGlowVAO) - gl.GenBuffers(1, &a.cursorGlowVBO) - - gl.BindVertexArray(a.cursorGlowVAO) - gl.BindBuffer(gl.ARRAY_BUFFER, a.cursorGlowVBO) - - glowQuadVertices := []float32{ - -1.0, -1.0, - 1.0, -1.0, - -1.0, 1.0, - 1.0, 1.0, - } - gl.BufferData(gl.ARRAY_BUFFER, len(glowQuadVertices)*4, gl.Ptr(glowQuadVertices), gl.STATIC_DRAW) - - gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 2*4, nil) - gl.EnableVertexAttribArray(0) - - gl.BindVertexArray(0) - - gl.Enable(gl.BLEND) - gl.BlendFunc(gl.SRC_ALPHA, gl.ONE) - gl.Enable(gl.PROGRAM_POINT_SIZE) - - return nil -} - -func compileShader(source string, shaderType uint32) (uint32, error) { - shader := gl.CreateShader(shaderType) - csources, free := gl.Strs(source) - gl.ShaderSource(shader, 1, csources, nil) - free() - gl.CompileShader(shader) - - var status int32 - gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) - if status == gl.FALSE { - var logLength int32 - gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) - logMsg := make([]byte, logLength) - gl.GetShaderInfoLog(shader, logLength, nil, &logMsg[0]) - log.Fatalf("Failed to compile shader: %s", logMsg) - } - - return shader, nil -} - -func (a *App) addPoint(x, y float32) { - newPoint := Point{X: x, Y: y, BornTime: time.Now()} - - shouldAdd := false - if len(a.points) == 0 { - shouldAdd = true - } else { - lastPoint := a.points[len(a.points)-1] - dx := newPoint.X - lastPoint.X - dy := newPoint.Y - lastPoint.Y - if dx*dx+dy*dy > 4 { - shouldAdd = true - - for range 3 { - angle := rand.Float64() * 2 * math.Pi - speed := rand.Float32()*50 + 20 - a.particles = append(a.particles, Particle{ - X: x + (rand.Float32()-0.5)*10, - Y: y + (rand.Float32()-0.5)*10, - VX: float32(math.Cos(angle)) * speed, - VY: float32(math.Sin(angle)) * speed, - Life: 1.0, - MaxLife: 1.0, - Size: rand.Float32()*15 + 10, - Hue: rand.Float32(), - }) - } - } - } - - const MAX_POINTS = 2048 - - if shouldAdd { - a.points = append(a.points, newPoint) - if len(a.points) > MAX_POINTS { - a.points = a.points[len(a.points)-MAX_POINTS:] - } - } -} - -func (a *App) spawnCursorSparkles(x, y float32) { - for range 3 { - angle := rand.Float64() * 2 * math.Pi - speed := rand.Float32()*80 + 40 - a.particles = append(a.particles, Particle{ - X: x + (rand.Float32()-0.5)*8, - Y: y + (rand.Float32()-0.5)*8, - VX: float32(math.Cos(angle)) * speed, - VY: float32(math.Sin(angle))*speed - 30, - Life: 0.8, - MaxLife: 0.8, - Size: rand.Float32()*8 + 6, - Hue: rand.Float32(), - }) - } -} - -func (a *App) spawnExitWisps(x, y float32) { - for range 8 { - angle := rand.Float64() * 2 * math.Pi - speed := rand.Float32()*150 + 80 - a.particles = append(a.particles, Particle{ - X: x + (rand.Float32()-0.5)*30, - Y: y + (rand.Float32()-0.5)*30, - VX: float32(math.Cos(angle)) * speed, - VY: float32(math.Sin(angle)) * speed, - Life: 1.2, - MaxLife: 1.2, - Size: rand.Float32()*12 + 8, - Hue: rand.Float32(), - }) - } -} - -func (a *App) updateParticles(dt float32) { - for i := 0; i < len(a.particles); i++ { - p := &a.particles[i] - p.X += p.VX * dt - p.Y += p.VY * dt - p.VY += 100 * dt - p.Life -= dt - - if p.Life <= 0 { - a.particles[i] = a.particles[len(a.particles)-1] - a.particles = a.particles[:len(a.particles)-1] - i-- - } - } -} - -func (a *App) updateCursor(window *WaylandWindow) { - x, y := window.GetCursorPos() - fx, fy := float32(x), float32(y) - - dx := fx - a.lastCursorX - dy := fy - a.lastCursorY - a.cursorVelocity = float32(math.Sqrt(float64(dx*dx + dy*dy))) - - velocityDiff := a.cursorVelocity - a.smoothVelocity - a.smoothVelocity += velocityDiff * 0.2 - - if a.cursorVelocity > 0.1 { - targetRotation := float32(math.Atan2(float64(dy), float64(dx))) - - angleDiff := targetRotation - a.smoothRotation - if angleDiff > math.Pi { - angleDiff -= 2 * math.Pi - } else if angleDiff < -math.Pi { - angleDiff += 2 * math.Pi - } - - velocityFactor := float32(math.Min(float64(a.smoothVelocity/5.0), 1.0)) - smoothFactor := 0.03 + velocityFactor*0.08 - a.smoothRotation += angleDiff * smoothFactor - } - - var targetDrawing float32 - if a.isDrawing { - targetDrawing = 1.0 - } else { - targetDrawing = 0.0 - } - - diff := targetDrawing - a.smoothDrawing - a.smoothDrawing += diff * 0.0375 - - a.lastCursorX = fx - a.lastCursorY = fy -} - -func (a *App) draw(window *WaylandWindow) { - gl.Clear(gl.COLOR_BUFFER_BIT) - - currentTime := float32(time.Since(a.startTime).Seconds()) - - a.drawBackground(currentTime, window) - - x, y := window.GetCursorPos() - a.drawCursorGlow(window, float32(x), float32(y), currentTime) - - for pass := range 3 { - thickness := float32(7 + pass*4) - alpha := float32(0.7 - float32(pass)*0.15) - a.drawLine(window, thickness, alpha, currentTime) - } - - a.drawParticles(window) -} - -func (a *App) drawLine(window *WaylandWindow, baseThickness, baseAlpha, currentTime float32) { - if len(a.points) < 2 { - return - } - - vertices := make([]float32, 0, len(a.points)*10) - - for i := range a.points { - age := float32(time.Since(a.points[i].BornTime).Seconds()) - fade := 1.0 - (age / 1.5) - if fade < 0 { - fade = 0 - } - alpha := fade * baseAlpha - - var perpX, perpY float32 - - if i == 0 { - dx := a.points[i+1].X - a.points[i].X - dy := a.points[i+1].Y - a.points[i].Y - length := float32(1.0) / float32(math.Sqrt(float64(dx*dx+dy*dy))) - perpX = -dy * length - perpY = dx * length - } else if i == len(a.points)-1 { - dx := a.points[i].X - a.points[i-1].X - dy := a.points[i].Y - a.points[i-1].Y - length := float32(1.0) / float32(math.Sqrt(float64(dx*dx+dy*dy))) - perpX = -dy * length - perpY = dx * length - } else { - dx1 := a.points[i].X - a.points[i-1].X - dy1 := a.points[i].Y - a.points[i-1].Y - len1 := float32(math.Sqrt(float64(dx1*dx1 + dy1*dy1))) - if len1 > 0 { - dx1 /= len1 - dy1 /= len1 - } - - dx2 := a.points[i+1].X - a.points[i].X - dy2 := a.points[i+1].Y - a.points[i].Y - len2 := float32(math.Sqrt(float64(dx2*dx2 + dy2*dy2))) - if len2 > 0 { - dx2 /= len2 - dy2 /= len2 - } - - avgDx := (dx1 + dx2) * 0.5 - avgDy := (dy1 + dy2) * 0.5 - avgLen := float32(math.Sqrt(float64(avgDx*avgDx + avgDy*avgDy))) - if avgLen > 0 { - avgDx /= avgLen - avgDy /= avgLen - } - - perpX = -avgDy - perpY = avgDx - } - - vertices = append(vertices, a.points[i].X, a.points[i].Y, perpX, perpY, alpha) - vertices = append(vertices, a.points[i].X, a.points[i].Y, -perpX, -perpY, alpha) - } - - cutoff := time.Now().Add(-1500 * time.Millisecond) - for len(a.points) > 0 && a.points[0].BornTime.Before(cutoff) { - a.points = a.points[1:] - } - - if len(vertices) == 0 { - return - } - - gl.BindBuffer(gl.ARRAY_BUFFER, a.vbo) - gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.DYNAMIC_DRAW) - - width, height := window.GetSize() - - gl.UseProgram(a.program) - resolutionLoc := gl.GetUniformLocation(a.program, gl.Str("resolution\x00")) - gl.Uniform2f(resolutionLoc, float32(width), float32(height)) - thicknessLoc := gl.GetUniformLocation(a.program, gl.Str("thickness\x00")) - gl.Uniform1f(thicknessLoc, baseThickness) - timeLoc := gl.GetUniformLocation(a.program, gl.Str("time\x00")) - gl.Uniform1f(timeLoc, currentTime) - - gl.BindVertexArray(a.vao) - gl.DrawArrays(gl.TRIANGLE_STRIP, 0, int32(len(a.points)*2)) - gl.BindVertexArray(0) -} - -func (a *App) drawParticles(window *WaylandWindow) { - if len(a.particles) == 0 { - return - } - - vertices := make([]float32, 0, len(a.particles)*6) - for _, p := range a.particles { - vertices = append(vertices, p.X, p.Y, p.Life, p.MaxLife, p.Size, p.Hue) - } - - gl.BindBuffer(gl.ARRAY_BUFFER, a.particleVBO) - gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.DYNAMIC_DRAW) - - width, height := window.GetSize() - - gl.UseProgram(a.particleProgram) - resolutionLoc := gl.GetUniformLocation(a.particleProgram, gl.Str("resolution\x00")) - gl.Uniform2f(resolutionLoc, float32(width), float32(height)) - - gl.BindVertexArray(a.particleVAO) - gl.DrawArrays(gl.POINTS, 0, int32(len(a.particles))) - gl.BindVertexArray(0) -} - -func (a *App) drawBackground(currentTime float32, window *WaylandWindow) { - fadeDuration := float32(1.0) - targetAlpha := float32(0.75) - - var alpha float32 - if currentTime < fadeDuration { - progress := currentTime / fadeDuration - easedProgress := 1.0 - (1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress) - alpha = easedProgress * targetAlpha - } else { - alpha = targetAlpha - } - - if a.isExiting { - exitDuration := float32(0.8) - elapsed := float32(time.Since(a.exitStartTime).Seconds()) - if elapsed < exitDuration { - progress := elapsed / exitDuration - easedProgress := 1.0 - (1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress)*(1.0-progress) - alpha *= (1.0 - easedProgress) - } else { - alpha = 0 - } - } - - x, y := window.GetCursorPos() - width, height := window.GetSize() - - gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) - - gl.UseProgram(a.bgProgram) - - alphaLoc := gl.GetUniformLocation(a.bgProgram, gl.Str("alpha\x00")) - gl.Uniform1f(alphaLoc, alpha) - - cursorPosLoc := gl.GetUniformLocation(a.bgProgram, gl.Str("cursorPos\x00")) - gl.Uniform2f(cursorPosLoc, float32(x), float32(float64(height)-y)) - - resolutionLoc := gl.GetUniformLocation(a.bgProgram, gl.Str("resolution\x00")) - gl.Uniform2f(resolutionLoc, float32(width), float32(height)) - - gl.BindVertexArray(a.bgVAO) - gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) - gl.BindVertexArray(0) - - gl.BlendFunc(gl.SRC_ALPHA, gl.ONE) -} - -func (a *App) drawCursorGlow(window *WaylandWindow, cursorX, cursorY, currentTime float32) { - width, height := window.GetSize() - - growDuration := float32(1.2) - var scale float32 - if currentTime < growDuration { - t := currentTime / growDuration - c4 := (2.0 * math.Pi) / 3.0 - if t == 0 { - scale = 0 - } else if t >= 1 { - scale = 1 - } else { - scale = float32(math.Pow(2, -10*float64(t))*math.Sin((float64(t)*10-0.75)*c4) + 1) - } - } else { - scale = 1.0 - } - - var exitProgress float32 - if a.isExiting { - exitDuration := float32(0.8) - elapsed := float32(time.Since(a.exitStartTime).Seconds()) - if elapsed < exitDuration { - t := elapsed / exitDuration - exitProgress = t * t * t - scale *= (1.0 - exitProgress) - } else { - exitProgress = 1.0 - scale = 0 - } - } - - gl.UseProgram(a.cursorGlowProgram) - - cursorPosLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("cursorPos\x00")) - gl.Uniform2f(cursorPosLoc, cursorX, cursorY) - - resolutionLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("resolution\x00")) - gl.Uniform2f(resolutionLoc, float32(width), float32(height)) - - glowSizeLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("glowSize\x00")) - gl.Uniform1f(glowSizeLoc, 80.0*scale) - - timeLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("time\x00")) - gl.Uniform1f(timeLoc, currentTime) - - velocityLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("velocity\x00")) - gl.Uniform1f(velocityLoc, a.smoothVelocity) - - rotationLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("rotation\x00")) - gl.Uniform1f(rotationLoc, a.smoothRotation) - - isDrawingLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("isDrawing\x00")) - gl.Uniform1f(isDrawingLoc, a.smoothDrawing) - - exitProgressLoc := gl.GetUniformLocation(a.cursorGlowProgram, gl.Str("exitProgress\x00")) - gl.Uniform1f(exitProgressLoc, exitProgress) - - gl.BindVertexArray(a.cursorGlowVAO) - gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) - gl.BindVertexArray(0) -} - -type GestureConfig struct { - Command string `json:"command"` - Templates [][]Point `json:"templates"` -} - -func getConfigPath() (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", err - } - configDir := filepath.Join(homeDir, ".config", "hexecute") - if err := os.MkdirAll(configDir, 0755); err != nil { - return "", err - } - return filepath.Join(configDir, "gestures.json"), nil -} - -func loadGestures() ([]GestureConfig, error) { - configFile, err := getConfigPath() - if err != nil { - return nil, err - } - - data, err := os.ReadFile(configFile) - if err != nil { - if os.IsNotExist(err) { - return []GestureConfig{}, nil - } - return nil, err - } - - var gestures []GestureConfig - if err := json.Unmarshal(data, &gestures); err != nil { - return nil, err - } - - return gestures, nil -} - -func saveGesture(command string, templates [][]Point) error { - configFile, err := getConfigPath() - if err != nil { - return err - } - - var gestures []GestureConfig - if data, err := os.ReadFile(configFile); err == nil { - json.Unmarshal(data, &gestures) - } - - newGesture := GestureConfig{ - Command: command, - Templates: templates, - } - - found := false - for i, g := range gestures { - if g.Command == command { - gestures[i] = newGesture - found = true - break - } - } - if !found { - gestures = append(gestures, newGesture) - } - - data, err := json.Marshal(gestures) - if err != nil { - return err - } - - return os.WriteFile(configFile, data, 0644) -} - -func executeCommand(command string) error { - if command == "" { - return nil - } - - cmd := exec.Command("sh", "-c", command) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Stdin = nil - cmd.Stdout = nil - cmd.Stderr = nil - - return cmd.Start() -} - -func (a *App) recognizeAndExecute(window *WaylandWindow, x, y float32) { - if len(a.points) < 5 { - log.Println("Gesture too short, ignoring") - return - } - - processed := ProcessStroke(a.points) - - bestMatch := -1 - bestScore := 0.0 - - for i, gesture := range a.savedGestures { - match, score := UnistrokeRecognise(processed, gesture.Templates) - log.Printf("Gesture %d (%s): template %d, score %.3f", i, gesture.Command, match, score) - - if score > bestScore { - bestScore = score - bestMatch = i - } - } - - if bestMatch >= 0 && bestScore > 0.6 { - command := a.savedGestures[bestMatch].Command - log.Printf("Matched gesture: %s (score: %.3f)", command, bestScore) - - if err := executeCommand(command); err != nil { - log.Printf("Failed to execute command: %v", err) - } else { - log.Printf("Executed: %s", command) - } - - a.isExiting = true - a.exitStartTime = time.Now() - window.DisableInput() - a.spawnExitWisps(x, y) - } else { - log.Printf("No confident match (best score: %.3f)", bestScore) - } -} - -func main() { - learnCommand := flag.String("learn", "", "Learn a new gesture for the specified command") - listGestures := flag.Bool("list", false, "List all registered gestures") - removeGesture := flag.String("remove", "", "Remove a gesture by command name") - flag.Parse() - - if flag.NArg() > 0 { - log.Fatalf("Unknown arguments: %v", flag.Args()) - } - - if *listGestures { - gestures, err := loadGestures() - if err != nil { - log.Fatal("Failed to load gestures:", err) - } - if len(gestures) == 0 { - println("No gestures registered") - } else { - println("Registered gestures:") - for _, g := range gestures { - println(" ", g.Command) - } - } - return - } - - if *removeGesture != "" { - gestures, err := loadGestures() - if err != nil { - log.Fatal("Failed to load gestures:", err) - } - - found := false - for i, g := range gestures { - if g.Command == *removeGesture { - gestures = append(gestures[:i], gestures[i+1:]...) - found = true - break - } - } - - if !found { - log.Fatalf("Gesture not found: %s", *removeGesture) - } - - configFile, err := getConfigPath() - if err != nil { - log.Fatal("Failed to get config path:", err) - } - - data, err := json.Marshal(gestures) - if err != nil { - log.Fatal("Failed to marshal gestures:", err) - } - - if err := os.WriteFile(configFile, data, 0644); err != nil { - log.Fatal("Failed to save gestures:", err) - } - - println("Removed gesture:", *removeGesture) - return - } - - window, err := NewWaylandWindow() - if err != nil { - log.Fatal("Failed to create Wayland window:", err) - } - defer window.Destroy() - - app := &App{startTime: time.Now()} - - if *learnCommand != "" { - app.learnMode = true - app.learnCommand = *learnCommand - log.Printf("Learn mode: Draw the gesture 3 times for command '%s'", *learnCommand) - } else { - gestures, err := loadGestures() - if err != nil { - log.Fatal("Failed to load gestures:", err) - } - app.savedGestures = gestures - log.Printf("Loaded %d gesture(s)", len(gestures)) - } - - if err := app.initGL(); err != nil { - log.Fatal("Failed to initialize OpenGL:", err) - } - - gl.ClearColor(0, 0, 0, 0) - - for i := 0; i < 5; i++ { - window.PollEvents() - gl.Clear(gl.COLOR_BUFFER_BIT) - window.SwapBuffers() - } - - x, y := window.GetCursorPos() - app.lastCursorX = float32(x) - app.lastCursorY = float32(y) - - lastTime := time.Now() - var wasPressed bool - - for !window.ShouldClose() { - now := time.Now() - dt := float32(now.Sub(lastTime).Seconds()) - lastTime = now - - window.PollEvents() - app.updateCursor(window) - - if key, state, hasKey := window.GetLastKey(); hasKey { - if state == 1 && key == 1 { - if !app.isExiting { - app.isExiting = true - app.exitStartTime = time.Now() - window.DisableInput() - x, y := window.GetCursorPos() - app.spawnExitWisps(float32(x), float32(y)) - } - } - window.ClearLastKey() - } - - if app.isExiting { - if time.Since(app.exitStartTime).Seconds() > 0.8 { - break - } - } - isPressed := window.GetMouseButton() - if isPressed && !wasPressed { - app.isDrawing = true - } else if !isPressed && wasPressed { - app.isDrawing = false - - if app.learnMode && len(app.points) > 0 { - processed := ProcessStroke(app.points) - app.learnGestures = append(app.learnGestures, processed) - app.learnCount++ - log.Printf("Captured gesture %d/3", app.learnCount) - - app.points = nil - - if app.learnCount >= 3 { - if err := saveGesture(app.learnCommand, app.learnGestures); err != nil { - log.Fatal("Failed to save gesture:", err) - } - log.Printf("Gesture saved for command: %s", app.learnCommand) - - app.isExiting = true - app.exitStartTime = time.Now() - window.DisableInput() - x, y := window.GetCursorPos() - app.spawnExitWisps(float32(x), float32(y)) - } - } else if !app.learnMode && len(app.points) > 0 { - x, y := window.GetCursorPos() - app.recognizeAndExecute(window, float32(x), float32(y)) - app.points = nil - } - } - wasPressed = isPressed - - if app.isDrawing { - x, y := window.GetCursorPos() - app.addPoint(float32(x), float32(y)) - app.spawnCursorSparkles(float32(x), float32(y)) - } - - app.updateParticles(dt) - app.draw(window) - window.SwapBuffers() - } -} diff --git a/pkg/wayland/.clangd b/pkg/wayland/.clangd new file mode 100644 index 0000000..0da6147 --- /dev/null +++ b/pkg/wayland/.clangd @@ -0,0 +1,4 @@ +Diagnostics: + Suppress: + - keyword_as_parameter + - expected_expression diff --git a/keyboard-shortcuts-inhibit-client.h b/pkg/wayland/keyboard-shortcuts-inhibit-client.h similarity index 62% rename from keyboard-shortcuts-inhibit-client.h rename to pkg/wayland/keyboard-shortcuts-inhibit-client.h index 7564fcc..637805e 100644 --- a/keyboard-shortcuts-inhibit-client.h +++ b/pkg/wayland/keyboard-shortcuts-inhibit-client.h @@ -3,17 +3,18 @@ #ifndef KEYBOARD_SHORTCUTS_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H #define KEYBOARD_SHORTCUTS_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H -#include -#include #include "wayland-client.h" +#include +#include -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif /** - * @page page_keyboard_shortcuts_inhibit_unstable_v1 The keyboard_shortcuts_inhibit_unstable_v1 protocol - * Protocol for inhibiting the compositor keyboard shortcuts + * @page page_keyboard_shortcuts_inhibit_unstable_v1 The + * keyboard_shortcuts_inhibit_unstable_v1 protocol Protocol for inhibiting the + * compositor keyboard shortcuts * * @section page_desc_keyboard_shortcuts_inhibit_unstable_v1 Description * @@ -33,8 +34,10 @@ extern "C" { * reset. * * @section page_ifaces_keyboard_shortcuts_inhibit_unstable_v1 Interfaces - * - @subpage page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1 - context object for keyboard grab_manager - * - @subpage page_iface_zwp_keyboard_shortcuts_inhibitor_v1 - context object for keyboard shortcuts inhibitor + * - @subpage page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1 - context + * object for keyboard grab_manager + * - @subpage page_iface_zwp_keyboard_shortcuts_inhibitor_v1 - context object + * for keyboard shortcuts inhibitor * @section page_copyright_keyboard_shortcuts_inhibit_unstable_v1 Copyright *
  *
@@ -68,24 +71,29 @@ struct zwp_keyboard_shortcuts_inhibitor_v1;
 #ifndef ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INTERFACE
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INTERFACE
 /**
- * @page page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1 zwp_keyboard_shortcuts_inhibit_manager_v1
- * @section page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1_desc Description
+ * @page page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1
+ * zwp_keyboard_shortcuts_inhibit_manager_v1
+ * @section page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1_desc
+ * Description
  *
  * A global interface used for inhibiting the compositor keyboard shortcuts.
  * @section page_iface_zwp_keyboard_shortcuts_inhibit_manager_v1_api API
  * See @ref iface_zwp_keyboard_shortcuts_inhibit_manager_v1.
  */
 /**
- * @defgroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1 The zwp_keyboard_shortcuts_inhibit_manager_v1 interface
+ * @defgroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1 The
+ * zwp_keyboard_shortcuts_inhibit_manager_v1 interface
  *
  * A global interface used for inhibiting the compositor keyboard shortcuts.
  */
-extern const struct wl_interface zwp_keyboard_shortcuts_inhibit_manager_v1_interface;
+extern const struct wl_interface
+    zwp_keyboard_shortcuts_inhibit_manager_v1_interface;
 #endif
 #ifndef ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_INTERFACE
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_INTERFACE
 /**
- * @page page_iface_zwp_keyboard_shortcuts_inhibitor_v1 zwp_keyboard_shortcuts_inhibitor_v1
+ * @page page_iface_zwp_keyboard_shortcuts_inhibitor_v1
+ * zwp_keyboard_shortcuts_inhibitor_v1
  * @section page_iface_zwp_keyboard_shortcuts_inhibitor_v1_desc Description
  *
  * A keyboard shortcuts inhibitor instructs the compositor to ignore
@@ -125,7 +133,8 @@ extern const struct wl_interface zwp_keyboard_shortcuts_inhibit_manager_v1_inter
  * See @ref iface_zwp_keyboard_shortcuts_inhibitor_v1.
  */
 /**
- * @defgroup iface_zwp_keyboard_shortcuts_inhibitor_v1 The zwp_keyboard_shortcuts_inhibitor_v1 interface
+ * @defgroup iface_zwp_keyboard_shortcuts_inhibitor_v1 The
+ * zwp_keyboard_shortcuts_inhibitor_v1 interface
  *
  * A keyboard shortcuts inhibitor instructs the compositor to ignore
  * its own keyboard shortcuts when the associated surface has keyboard
@@ -167,17 +176,16 @@ extern const struct wl_interface zwp_keyboard_shortcuts_inhibitor_v1_interface;
 #ifndef ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ENUM
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ENUM
 enum zwp_keyboard_shortcuts_inhibit_manager_v1_error {
-	/**
-	 * the shortcuts are already inhibited for this surface
-	 */
-	ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED = 0,
+  /**
+   * the shortcuts are already inhibited for this surface
+   */
+  ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED = 0,
 };
 #endif /* ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ENUM */
 
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_DESTROY 0
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INHIBIT_SHORTCUTS 1
 
-
 /**
  * @ingroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1
  */
@@ -185,26 +193,31 @@ enum zwp_keyboard_shortcuts_inhibit_manager_v1_error {
 /**
  * @ingroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1
  */
-#define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INHIBIT_SHORTCUTS_SINCE_VERSION 1
+#define ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INHIBIT_SHORTCUTS_SINCE_VERSION \
+  1
 
 /** @ingroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1 */
-static inline void
-zwp_keyboard_shortcuts_inhibit_manager_v1_set_user_data(struct zwp_keyboard_shortcuts_inhibit_manager_v1 *zwp_keyboard_shortcuts_inhibit_manager_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1, user_data);
+static inline void zwp_keyboard_shortcuts_inhibit_manager_v1_set_user_data(
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1
+        *zwp_keyboard_shortcuts_inhibit_manager_v1,
+    void *user_data) {
+  wl_proxy_set_user_data(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1, user_data);
 }
 
 /** @ingroup iface_zwp_keyboard_shortcuts_inhibit_manager_v1 */
-static inline void *
-zwp_keyboard_shortcuts_inhibit_manager_v1_get_user_data(struct zwp_keyboard_shortcuts_inhibit_manager_v1 *zwp_keyboard_shortcuts_inhibit_manager_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1);
+static inline void *zwp_keyboard_shortcuts_inhibit_manager_v1_get_user_data(
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1
+        *zwp_keyboard_shortcuts_inhibit_manager_v1) {
+  return wl_proxy_get_user_data(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1);
 }
 
-static inline uint32_t
-zwp_keyboard_shortcuts_inhibit_manager_v1_get_version(struct zwp_keyboard_shortcuts_inhibit_manager_v1 *zwp_keyboard_shortcuts_inhibit_manager_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1);
+static inline uint32_t zwp_keyboard_shortcuts_inhibit_manager_v1_get_version(
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1
+        *zwp_keyboard_shortcuts_inhibit_manager_v1) {
+  return wl_proxy_get_version(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1);
 }
 
 /**
@@ -212,11 +225,15 @@ zwp_keyboard_shortcuts_inhibit_manager_v1_get_version(struct zwp_keyboard_shortc
  *
  * Destroy the keyboard shortcuts inhibitor manager.
  */
-static inline void
-zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(struct zwp_keyboard_shortcuts_inhibit_manager_v1 *zwp_keyboard_shortcuts_inhibit_manager_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1,
-			 ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1), WL_MARSHAL_FLAG_DESTROY);
+static inline void zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1
+        *zwp_keyboard_shortcuts_inhibit_manager_v1) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1,
+      ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_DESTROY, NULL,
+      wl_proxy_get_version(
+          (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1),
+      WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -229,14 +246,21 @@ zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(struct zwp_keyboard_shortcuts_
  * a protocol error "already_inhibited" is raised by the compositor.
  */
 static inline struct zwp_keyboard_shortcuts_inhibitor_v1 *
-zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(struct zwp_keyboard_shortcuts_inhibit_manager_v1 *zwp_keyboard_shortcuts_inhibit_manager_v1, struct wl_surface *surface, struct wl_seat *seat)
-{
-	struct wl_proxy *id;
+zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1
+        *zwp_keyboard_shortcuts_inhibit_manager_v1,
+    struct wl_surface *surface, struct wl_seat *seat) {
+  struct wl_proxy *id;
 
-	id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1,
-			 ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INHIBIT_SHORTCUTS, &zwp_keyboard_shortcuts_inhibitor_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_keyboard_shortcuts_inhibit_manager_v1), 0, NULL, surface, seat);
+  id = wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1,
+      ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_INHIBIT_SHORTCUTS,
+      &zwp_keyboard_shortcuts_inhibitor_v1_interface,
+      wl_proxy_get_version(
+          (struct wl_proxy *)zwp_keyboard_shortcuts_inhibit_manager_v1),
+      0, NULL, surface, seat);
 
-	return (struct zwp_keyboard_shortcuts_inhibitor_v1 *) id;
+  return (struct zwp_keyboard_shortcuts_inhibitor_v1 *)id;
 }
 
 /**
@@ -244,42 +268,44 @@ zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(struct zwp_keyboard_
  * @struct zwp_keyboard_shortcuts_inhibitor_v1_listener
  */
 struct zwp_keyboard_shortcuts_inhibitor_v1_listener {
-	/**
-	 * shortcuts are inhibited
-	 *
-	 * This event indicates that the shortcut inhibitor is active.
-	 *
-	 * The compositor sends this event every time compositor shortcuts
-	 * are inhibited on behalf of the surface. When active, the client
-	 * may receive input events normally reserved by the compositor
-	 * (see zwp_keyboard_shortcuts_inhibitor_v1).
-	 *
-	 * This occurs typically when the initial request
-	 * "inhibit_shortcuts" first becomes active or when the user
-	 * instructs the compositor to re-enable and existing shortcuts
-	 * inhibitor using any mechanism offered by the compositor.
-	 */
-	void (*active)(void *data,
-		       struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1);
-	/**
-	 * shortcuts are restored
-	 *
-	 * This event indicates that the shortcuts inhibitor is inactive,
-	 * normal shortcuts processing is restored by the compositor.
-	 */
-	void (*inactive)(void *data,
-			 struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1);
+  /**
+   * shortcuts are inhibited
+   *
+   * This event indicates that the shortcut inhibitor is active.
+   *
+   * The compositor sends this event every time compositor shortcuts
+   * are inhibited on behalf of the surface. When active, the client
+   * may receive input events normally reserved by the compositor
+   * (see zwp_keyboard_shortcuts_inhibitor_v1).
+   *
+   * This occurs typically when the initial request
+   * "inhibit_shortcuts" first becomes active or when the user
+   * instructs the compositor to re-enable and existing shortcuts
+   * inhibitor using any mechanism offered by the compositor.
+   */
+  void (*active)(void *data, struct zwp_keyboard_shortcuts_inhibitor_v1
+                                 *zwp_keyboard_shortcuts_inhibitor_v1);
+  /**
+   * shortcuts are restored
+   *
+   * This event indicates that the shortcuts inhibitor is inactive,
+   * normal shortcuts processing is restored by the compositor.
+   */
+  void (*inactive)(void *data, struct zwp_keyboard_shortcuts_inhibitor_v1
+                                   *zwp_keyboard_shortcuts_inhibitor_v1);
 };
 
 /**
  * @ingroup iface_zwp_keyboard_shortcuts_inhibitor_v1
  */
-static inline int
-zwp_keyboard_shortcuts_inhibitor_v1_add_listener(struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1,
-						 const struct zwp_keyboard_shortcuts_inhibitor_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1,
-				     (void (**)(void)) listener, data);
+static inline int zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
+    struct zwp_keyboard_shortcuts_inhibitor_v1
+        *zwp_keyboard_shortcuts_inhibitor_v1,
+    const struct zwp_keyboard_shortcuts_inhibitor_v1_listener *listener,
+    void *data) {
+  return wl_proxy_add_listener(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1,
+      (void (**)(void))listener, data);
 }
 
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_DESTROY 0
@@ -299,23 +325,27 @@ zwp_keyboard_shortcuts_inhibitor_v1_add_listener(struct zwp_keyboard_shortcuts_i
 #define ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_DESTROY_SINCE_VERSION 1
 
 /** @ingroup iface_zwp_keyboard_shortcuts_inhibitor_v1 */
-static inline void
-zwp_keyboard_shortcuts_inhibitor_v1_set_user_data(struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1, user_data);
+static inline void zwp_keyboard_shortcuts_inhibitor_v1_set_user_data(
+    struct zwp_keyboard_shortcuts_inhibitor_v1
+        *zwp_keyboard_shortcuts_inhibitor_v1,
+    void *user_data) {
+  wl_proxy_set_user_data((struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1,
+                         user_data);
 }
 
 /** @ingroup iface_zwp_keyboard_shortcuts_inhibitor_v1 */
-static inline void *
-zwp_keyboard_shortcuts_inhibitor_v1_get_user_data(struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1);
+static inline void *zwp_keyboard_shortcuts_inhibitor_v1_get_user_data(
+    struct zwp_keyboard_shortcuts_inhibitor_v1
+        *zwp_keyboard_shortcuts_inhibitor_v1) {
+  return wl_proxy_get_user_data(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1);
 }
 
-static inline uint32_t
-zwp_keyboard_shortcuts_inhibitor_v1_get_version(struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1);
+static inline uint32_t zwp_keyboard_shortcuts_inhibitor_v1_get_version(
+    struct zwp_keyboard_shortcuts_inhibitor_v1
+        *zwp_keyboard_shortcuts_inhibitor_v1) {
+  return wl_proxy_get_version(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1);
 }
 
 /**
@@ -323,14 +353,18 @@ zwp_keyboard_shortcuts_inhibitor_v1_get_version(struct zwp_keyboard_shortcuts_in
  *
  * Remove the keyboard shortcuts inhibitor from the associated wl_surface.
  */
-static inline void
-zwp_keyboard_shortcuts_inhibitor_v1_destroy(struct zwp_keyboard_shortcuts_inhibitor_v1 *zwp_keyboard_shortcuts_inhibitor_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1,
-			 ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_keyboard_shortcuts_inhibitor_v1), WL_MARSHAL_FLAG_DESTROY);
+static inline void zwp_keyboard_shortcuts_inhibitor_v1_destroy(
+    struct zwp_keyboard_shortcuts_inhibitor_v1
+        *zwp_keyboard_shortcuts_inhibitor_v1) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1,
+      ZWP_KEYBOARD_SHORTCUTS_INHIBITOR_V1_DESTROY, NULL,
+      wl_proxy_get_version(
+          (struct wl_proxy *)zwp_keyboard_shortcuts_inhibitor_v1),
+      WL_MARSHAL_FLAG_DESTROY);
 }
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 }
 #endif
 
diff --git a/pkg/wayland/wayland.c b/pkg/wayland/wayland.c
new file mode 100644
index 0000000..93b07ad
--- /dev/null
+++ b/pkg/wayland/wayland.c
@@ -0,0 +1,416 @@
+#include "wayland.h"
+#include "keyboard-shortcuts-inhibit-client.h"
+#include "wlr-layer-shell-client.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface wl_output_interface;
+extern const struct wl_interface wl_surface_interface;
+extern const struct wl_interface zwlr_layer_surface_v1_interface;
+
+static const struct wl_interface xdg_popup_interface = {
+    "xdg_popup", 0, 0, NULL, 0, NULL,
+};
+
+static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &zwlr_layer_surface_v1_interface,
+    &wl_surface_interface,
+    &wl_output_interface,
+    NULL,
+    NULL,
+    &xdg_popup_interface,
+};
+
+static const struct wl_message zwlr_layer_shell_v1_requests[] = {
+    {"get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4},
+    {"destroy", "3", wlr_layer_shell_unstable_v1_types + 0},
+};
+
+WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = {
+    "zwlr_layer_shell_v1", 4, 2, zwlr_layer_shell_v1_requests, 0, NULL,
+};
+
+static const struct wl_message zwlr_layer_surface_v1_requests[] = {
+    {"set_size", "uu", wlr_layer_shell_unstable_v1_types + 0},
+    {"set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0},
+    {"set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0},
+    {"set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0},
+    {"set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0},
+    {"get_popup", "o", wlr_layer_shell_unstable_v1_types + 9},
+    {"ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0},
+    {"destroy", "", wlr_layer_shell_unstable_v1_types + 0},
+    {"set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0},
+};
+
+static const struct wl_message zwlr_layer_surface_v1_events[] = {
+    {"configure", "uuu", wlr_layer_shell_unstable_v1_types + 0},
+    {"closed", "", wlr_layer_shell_unstable_v1_types + 0},
+};
+
+WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = {
+    "zwlr_layer_surface_v1",        4, 9,
+    zwlr_layer_surface_v1_requests, 2, zwlr_layer_surface_v1_events,
+};
+
+static const struct wl_interface
+    *keyboard_shortcuts_inhibit_unstable_v1_types[] = {
+        &zwp_keyboard_shortcuts_inhibitor_v1_interface,
+        &wl_surface_interface,
+        &wl_seat_interface,
+};
+
+static const struct wl_message
+    zwp_keyboard_shortcuts_inhibit_manager_v1_requests[] = {
+        {"destroy", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0},
+        {"inhibit_shortcuts", "noo",
+         keyboard_shortcuts_inhibit_unstable_v1_types + 0},
+};
+
+WL_PRIVATE const struct wl_interface
+    zwp_keyboard_shortcuts_inhibit_manager_v1_interface = {
+        "zwp_keyboard_shortcuts_inhibit_manager_v1",        1, 2,
+        zwp_keyboard_shortcuts_inhibit_manager_v1_requests, 0, NULL,
+};
+
+static const struct wl_message zwp_keyboard_shortcuts_inhibitor_v1_requests[] =
+    {
+        {"destroy", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0},
+};
+
+static const struct wl_message zwp_keyboard_shortcuts_inhibitor_v1_events[] = {
+    {"active", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0},
+    {"inactive", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0},
+};
+
+WL_PRIVATE const struct wl_interface
+    zwp_keyboard_shortcuts_inhibitor_v1_interface = {
+        "zwp_keyboard_shortcuts_inhibitor_v1",
+        1,
+        1,
+        zwp_keyboard_shortcuts_inhibitor_v1_requests,
+        2,
+        zwp_keyboard_shortcuts_inhibitor_v1_events,
+};
+
+struct wl_compositor *compositor = NULL;
+struct zwlr_layer_shell_v1 *layer_shell = NULL;
+struct wl_seat *seat = NULL;
+struct wl_pointer *pointer = NULL;
+struct wl_keyboard *keyboard = NULL;
+struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager =
+    NULL;
+struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibitor = NULL;
+struct zwlr_layer_surface_v1 *layer_surface_global = NULL;
+struct xkb_context *xkb_context;
+struct xkb_keymap *xkb_keymap;
+struct xkb_state *xkb_state;
+int32_t width_global = 0;
+int32_t height_global = 0;
+
+void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
+                             uint32_t serial, uint32_t width, uint32_t height) {
+  width_global = width;
+  height_global = height;
+  zwlr_layer_surface_v1_ack_configure(surface, serial);
+}
+
+void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) {}
+
+static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+    .configure = layer_surface_configure,
+    .closed = layer_surface_closed,
+};
+
+// Forward declarations for seat
+void seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities);
+void seat_name(void *data, struct wl_seat *seat, const char *name);
+
+static const struct wl_seat_listener seat_listener = {
+    .capabilities = seat_capabilities,
+    .name = seat_name,
+};
+
+void registry_global(void *data, struct wl_registry *registry, uint32_t name,
+                     const char *interface, uint32_t version) {
+  if (strcmp(interface, "wl_compositor") == 0) {
+    compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
+  } else if (strcmp(interface, "zwlr_layer_shell_v1") == 0) {
+    layer_shell = (struct zwlr_layer_shell_v1 *)wl_registry_bind(
+        registry, name, &zwlr_layer_shell_v1_interface, 1);
+  } else if (strcmp(interface, "wl_seat") == 0) {
+    seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
+    wl_seat_add_listener(seat, &seat_listener, NULL);
+  } else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") ==
+             0) {
+    shortcuts_inhibit_manager =
+        (struct zwp_keyboard_shortcuts_inhibit_manager_v1 *)wl_registry_bind(
+            registry, name,
+            &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
+  }
+}
+
+void registry_global_remove(void *data, struct wl_registry *registry,
+                            uint32_t name) {}
+
+static const struct wl_registry_listener registry_listener = {
+    .global = registry_global,
+    .global_remove = registry_global_remove,
+};
+
+struct wl_registry *get_registry(struct wl_display *display) {
+  return wl_display_get_registry(display);
+}
+
+void add_registry_listener(struct wl_registry *registry) {
+  wl_registry_add_listener(registry, ®istry_listener, NULL);
+}
+
+struct wl_surface *surface_global = NULL;
+
+struct zwlr_layer_surface_v1 *create_layer_surface(struct wl_surface *surface) {
+  surface_global = surface;
+
+  layer_surface_global = zwlr_layer_shell_v1_get_layer_surface(
+      layer_shell, surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+      "hexecute");
+
+  zwlr_layer_surface_v1_set_anchor(layer_surface_global,
+                                   ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
+
+  zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_global, -1);
+
+  zwlr_layer_surface_v1_set_keyboard_interactivity(
+      layer_surface_global,
+      ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE);
+
+  zwlr_layer_surface_v1_add_listener(layer_surface_global,
+                                     &layer_surface_listener, NULL);
+
+  wl_surface_commit(surface);
+
+  return layer_surface_global;
+}
+
+void set_input_region(int32_t width, int32_t height) {
+  if (surface_global) {
+    struct wl_region *region = wl_compositor_create_region(compositor);
+    wl_region_add(region, 0, 0, width, height);
+    wl_surface_set_input_region(surface_global, region);
+    wl_region_destroy(region);
+    wl_surface_commit(surface_global);
+  }
+}
+
+void disable_all_input() {
+  if (shortcuts_inhibitor) {
+    zwp_keyboard_shortcuts_inhibitor_v1_destroy(shortcuts_inhibitor);
+    shortcuts_inhibitor = NULL;
+  }
+
+  if (layer_surface_global) {
+    zwlr_layer_surface_v1_set_keyboard_interactivity(
+        layer_surface_global,
+        ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE);
+  }
+
+  if (surface_global) {
+    struct wl_region *region = wl_compositor_create_region(compositor);
+    wl_surface_set_input_region(surface_global, region);
+    wl_region_destroy(region);
+    wl_surface_commit(surface_global);
+  }
+}
+
+static int button_state = 0;
+static double mouse_x = 0;
+static double mouse_y = 0;
+
+void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
+                   struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) {
+  mouse_x = wl_fixed_to_double(x);
+  mouse_y = wl_fixed_to_double(y);
+  wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
+}
+
+void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial,
+                   struct wl_surface *surface) {}
+
+void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time,
+                    wl_fixed_t x, wl_fixed_t y) {
+  mouse_x = wl_fixed_to_double(x);
+  mouse_y = wl_fixed_to_double(y);
+}
+
+void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+                    uint32_t time, uint32_t button, uint32_t state) {
+  if (button == 272) {
+    button_state = state;
+  }
+}
+
+void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time,
+                  uint32_t axis, wl_fixed_t value) {}
+
+void pointer_frame(void *data, struct wl_pointer *pointer) {}
+
+void pointer_axis_source(void *data, struct wl_pointer *pointer,
+                         uint32_t source) {}
+
+void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time,
+                       uint32_t axis) {}
+
+void pointer_axis_discrete(void *data, struct wl_pointer *pointer,
+                           uint32_t axis, int32_t discrete) {}
+
+static const struct wl_pointer_listener pointer_listener = {
+    .enter = pointer_enter,
+    .leave = pointer_leave,
+    .motion = pointer_motion,
+    .button = pointer_button,
+    .axis = pointer_axis,
+    .frame = pointer_frame,
+    .axis_source = pointer_axis_source,
+    .axis_stop = pointer_axis_stop,
+    .axis_discrete = pointer_axis_discrete,
+};
+
+static uint32_t last_key = 0;
+static uint32_t last_key_state = 0;
+
+void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
+                     int32_t fd, uint32_t size) {
+  if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+    close(fd);
+    return;
+  }
+
+  char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (map_shm == MAP_FAILED) {
+    close(fd);
+    return;
+  }
+
+  xkb_keymap = xkb_keymap_new_from_string(xkb_context, map_shm,
+                                          XKB_KEYMAP_FORMAT_TEXT_V1,
+                                          XKB_KEYMAP_COMPILE_NO_FLAGS);
+  munmap(map_shm, size);
+  close(fd);
+
+  if (!xkb_keymap) {
+    return;
+  }
+
+  xkb_state = xkb_state_new(xkb_keymap);
+  if (!xkb_state) {
+    return;
+  }
+}
+
+void keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                    struct wl_surface *surface, struct wl_array *keys) {}
+
+void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                    struct wl_surface *surface) {}
+
+void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                  uint32_t time, uint32_t key, uint32_t state) {
+  if (xkb_state) {
+    xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, key + 8);
+    if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+      last_key = sym;
+      last_key_state = 1;
+    } else {
+      last_key = 0;
+      last_key_state = 0;
+    }
+  }
+}
+
+void keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
+                        uint32_t serial, uint32_t mods_depressed,
+                        uint32_t mods_latched, uint32_t mods_locked,
+                        uint32_t group) {
+  if (xkb_state) {
+    xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked,
+                          0, 0, group);
+  }
+}
+
+void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard,
+                          int32_t rate, int32_t delay) {}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+    .keymap = keyboard_keymap,
+    .enter = keyboard_enter,
+    .leave = keyboard_leave,
+    .key = keyboard_key,
+    .modifiers = keyboard_modifiers,
+    .repeat_info = keyboard_repeat_info,
+};
+
+// Seat listener
+void seat_capabilities(void *data, struct wl_seat *seat,
+                       uint32_t capabilities) {
+  if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
+    pointer = wl_seat_get_pointer(seat);
+    wl_pointer_add_listener(pointer, &pointer_listener, NULL);
+  }
+
+  if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
+    keyboard = wl_seat_get_keyboard(seat);
+    wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
+
+    if (shortcuts_inhibit_manager && surface_global && !shortcuts_inhibitor) {
+      shortcuts_inhibitor =
+          zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
+              shortcuts_inhibit_manager, surface_global, seat);
+    }
+  }
+}
+
+void seat_name(void *data, struct wl_seat *seat, const char *name) {}
+
+int get_button_state() { return button_state; }
+
+void get_mouse_pos(double *x, double *y) {
+  *x = mouse_x;
+  *y = mouse_y;
+}
+
+void get_dimensions(int32_t *w, int32_t *h) {
+  *w = width_global;
+  *h = height_global;
+}
+
+uint32_t get_last_key() { return last_key; }
+
+uint32_t get_last_key_state() { return last_key_state; }
+
+void clear_last_key() {
+  last_key = 0;
+  last_key_state = 0;
+}
+
+EGLNativeWindowType native_window(struct wl_egl_window *egl_window) {
+  return (EGLNativeWindowType)egl_window;
+}
diff --git a/pkg/wayland/wayland.go b/pkg/wayland/wayland.go
new file mode 100644
index 0000000..cf52f8f
--- /dev/null
+++ b/pkg/wayland/wayland.go
@@ -0,0 +1,223 @@
+package wayland
+
+/*
+#cgo pkg-config: wayland-client wayland-egl egl gl xkbcommon
+#cgo LDFLAGS: -lwayland-client -lwayland-egl -lEGL -lGL -lxkbcommon
+#cgo CFLAGS: -I.
+#include "wayland.h"
+*/
+import "C"
+import (
+	"fmt"
+)
+
+type WaylandError struct {
+	msg string
+}
+
+func (e *WaylandError) Error() string {
+	return e.msg
+}
+
+type WaylandWindow struct {
+	display       *C.struct_wl_display
+	registry      *C.struct_wl_registry
+	surface       *C.struct_wl_surface
+	layerSurface  *C.struct_zwlr_layer_surface_v1
+	eglWindow     *C.struct_wl_egl_window
+	eglDisplay    C.EGLDisplay
+	eglContext    C.EGLContext
+	eglSurface    C.EGLSurface
+	width, height int32
+}
+
+func NewWaylandWindow() (*WaylandWindow, error) {
+	w := &WaylandWindow{}
+
+	C.xkb_context = C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS)
+	if C.xkb_context == nil {
+		return nil, &WaylandError{"failed to create xkb context"}
+	}
+
+	w.display = C.wl_display_connect(nil)
+	if w.display == nil {
+		return nil, &WaylandError{"failed to connect to Wayland display"}
+	}
+
+	w.registry = C.get_registry(w.display)
+	C.add_registry_listener(w.registry)
+	C.wl_display_roundtrip(w.display)
+	if C.compositor == nil {
+		return nil, &WaylandError{"compositor not available"}
+	}
+	if C.layer_shell == nil {
+		return nil, &WaylandError{"layer shell not available"}
+	}
+
+	w.surface = C.wl_compositor_create_surface(C.compositor)
+	if w.surface == nil {
+		return nil, &WaylandError{"failed to create surface"}
+	}
+
+	w.layerSurface = C.create_layer_surface(w.surface)
+
+	C.wl_display_roundtrip(w.display)
+
+	var width, height C.int32_t
+	C.get_dimensions(&width, &height)
+	w.width = int32(width)
+	w.height = int32(height)
+
+	if w.width == 0 || w.height == 0 {
+		w.width = 1920
+		w.height = 1080
+	}
+
+	C.wl_display_roundtrip(w.display)
+
+	C.set_input_region(C.int32_t(w.width), C.int32_t(w.height))
+
+	if err := w.initEGL(); err != nil {
+		return nil, err
+	}
+
+	C.wl_surface_commit(w.surface)
+	C.wl_display_flush(w.display)
+
+	C.wl_display_roundtrip(w.display)
+	C.wl_display_roundtrip(w.display)
+	C.wl_display_flush(w.display)
+
+	return w, nil
+}
+
+func (w *WaylandWindow) initEGL() error {
+	w.eglWindow = C.wl_egl_window_create(w.surface, C.int(w.width), C.int(w.height))
+	if w.eglWindow == nil {
+		return fmt.Errorf("failed to create EGL window")
+	}
+
+	w.eglDisplay = C.eglGetDisplay(C.EGLNativeDisplayType(w.display))
+	if w.eglDisplay == C.EGLDisplay(C.EGL_NO_DISPLAY) {
+		return fmt.Errorf("failed to get EGL display")
+	}
+
+	var major, minor C.EGLint
+	if C.eglInitialize(w.eglDisplay, &major, &minor) == C.EGL_FALSE {
+		return fmt.Errorf("failed to initialize EGL")
+	}
+
+	configAttribs := []C.EGLint{
+		C.EGL_SURFACE_TYPE, C.EGL_WINDOW_BIT,
+		C.EGL_RED_SIZE, 8,
+		C.EGL_GREEN_SIZE, 8,
+		C.EGL_BLUE_SIZE, 8,
+		C.EGL_ALPHA_SIZE, 8,
+		C.EGL_RENDERABLE_TYPE, C.EGL_OPENGL_BIT,
+		C.EGL_NONE,
+	}
+
+	var config C.EGLConfig
+	var numConfigs C.EGLint
+	if C.eglChooseConfig(w.eglDisplay, &configAttribs[0], &config, 1, &numConfigs) == C.EGL_FALSE {
+		return fmt.Errorf("failed to choose EGL config")
+	}
+
+	C.eglBindAPI(C.EGL_OPENGL_API)
+	contextAttribs := []C.EGLint{
+		C.EGL_CONTEXT_MAJOR_VERSION, 4,
+		C.EGL_CONTEXT_MINOR_VERSION, 1,
+		C.EGL_CONTEXT_OPENGL_PROFILE_MASK, C.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+		C.EGL_NONE,
+	}
+
+	w.eglContext = C.eglCreateContext(w.eglDisplay, config, nil, &contextAttribs[0])
+	if w.eglContext == nil {
+		return fmt.Errorf("failed to create EGL context")
+	}
+
+	w.eglSurface = C.eglCreateWindowSurface(
+		w.eglDisplay,
+		config,
+		C.native_window(w.eglWindow),
+		nil,
+	)
+	if w.eglSurface == nil {
+		return fmt.Errorf("failed to create EGL surface")
+	}
+
+	if C.eglMakeCurrent(w.eglDisplay, w.eglSurface, w.eglSurface, w.eglContext) == C.EGL_FALSE {
+		return fmt.Errorf("failed to make EGL context current")
+	}
+
+	return nil
+}
+
+func (w *WaylandWindow) GetSize() (int, int) {
+	var width, height C.int32_t
+	C.get_dimensions(&width, &height)
+	if width > 0 && height > 0 {
+		w.width = int32(width)
+		w.height = int32(height)
+	}
+	return int(w.width), int(w.height)
+}
+
+func (w *WaylandWindow) ShouldClose() bool {
+	return false
+}
+
+func (w *WaylandWindow) SwapBuffers() {
+	C.eglSwapBuffers(w.eglDisplay, w.eglSurface)
+}
+
+func (w *WaylandWindow) PollEvents() {
+	C.wl_display_flush(w.display)
+	C.wl_display_dispatch_pending(w.display)
+}
+
+func (w *WaylandWindow) GetCursorPos() (float64, float64) {
+	var x, y C.double
+	C.get_mouse_pos(&x, &y)
+	return float64(x), float64(y)
+}
+
+func (w *WaylandWindow) GetMouseButton() bool {
+	state := C.get_button_state()
+	return state == 1
+}
+
+func (w *WaylandWindow) DisableInput() {
+	C.disable_all_input()
+}
+
+func (w *WaylandWindow) GetLastKey() (uint32, uint32, bool) {
+	key := uint32(C.get_last_key())
+	state := uint32(C.get_last_key_state())
+	return key, state, key != 0
+}
+
+func (w *WaylandWindow) ClearLastKey() {
+	C.clear_last_key()
+}
+
+func (w *WaylandWindow) Destroy() {
+	if w.eglContext != C.EGLContext(C.EGL_NO_CONTEXT) {
+		C.eglDestroyContext(w.eglDisplay, w.eglContext)
+	}
+	if w.eglSurface != C.EGLSurface(C.EGL_NO_SURFACE) {
+		C.eglDestroySurface(w.eglDisplay, w.eglSurface)
+	}
+	if w.eglWindow != nil {
+		C.wl_egl_window_destroy(w.eglWindow)
+	}
+	if w.eglDisplay != C.EGLDisplay(C.EGL_NO_DISPLAY) {
+		C.eglTerminate(w.eglDisplay)
+	}
+	if w.surface != nil {
+		C.wl_surface_destroy(w.surface)
+	}
+	if w.display != nil {
+		C.wl_display_disconnect(w.display)
+	}
+}
diff --git a/pkg/wayland/wayland.h b/pkg/wayland/wayland.h
new file mode 100644
index 0000000..4e685e9
--- /dev/null
+++ b/pkg/wayland/wayland.h
@@ -0,0 +1,77 @@
+#ifndef WAYLAND_H
+#define WAYLAND_H
+
+#include "wlr-layer-shell-client.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
+                             uint32_t serial, uint32_t width, uint32_t height);
+void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface);
+void seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities);
+void seat_name(void *data, struct wl_seat *seat, const char *name);
+void registry_global(void *data, struct wl_registry *registry, uint32_t name,
+                     const char *interface, uint32_t version);
+void registry_global_remove(void *data, struct wl_registry *registry,
+                            uint32_t name);
+struct wl_registry *get_registry(struct wl_display *display);
+void add_registry_listener(struct wl_registry *registry);
+struct zwlr_layer_surface_v1 *create_layer_surface(struct wl_surface *surface);
+void set_input_region(int32_t width, int32_t height);
+void disable_all_input();
+void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
+                   struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y);
+void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial,
+                   struct wl_surface *surface);
+void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time,
+                    wl_fixed_t x, wl_fixed_t y);
+void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+                    uint32_t time, uint32_t button, uint32_t state);
+void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time,
+                  uint32_t axis, wl_fixed_t value);
+void pointer_frame(void *data, struct wl_pointer *pointer);
+void pointer_axis_source(void *data, struct wl_pointer *pointer,
+                         uint32_t source);
+void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time,
+                       uint32_t axis);
+void pointer_axis_discrete(void *data, struct wl_pointer *pointer,
+                           uint32_t axis, int32_t discrete);
+void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
+                     int32_t fd, uint32_t size);
+void keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                    struct wl_surface *surface, struct wl_array *keys);
+void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                    struct wl_surface *surface);
+void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
+                  uint32_t time, uint32_t key, uint32_t state);
+void keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
+                        uint32_t serial, uint32_t mods_depressed,
+                        uint32_t mods_latched, uint32_t mods_locked,
+                        uint32_t group);
+void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard,
+                          int32_t rate, int32_t delay);
+int get_button_state();
+void get_mouse_pos(double *x, double *y);
+void get_dimensions(int32_t *w, int32_t *h);
+uint32_t get_last_key();
+uint32_t get_last_key_state();
+void clear_last_key();
+EGLNativeWindowType native_window(struct wl_egl_window *egl_window);
+
+extern struct wl_compositor *compositor;
+extern struct zwlr_layer_shell_v1 *layer_shell;
+extern struct wl_seat *seat;
+extern struct wl_pointer *pointer;
+extern struct wl_keyboard *keyboard;
+extern struct zwp_keyboard_shortcuts_inhibit_manager_v1
+    *shortcuts_inhibit_manager;
+extern struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibitor;
+extern struct zwlr_layer_surface_v1 *layer_surface_global;
+extern struct xkb_context *xkb_context;
+extern struct xkb_keymap *xkb_keymap;
+extern struct xkb_state *xkb_state;
+
+#endif // WAYLAND_H
diff --git a/wlr-layer-shell-client.h b/pkg/wayland/wlr-layer-shell-client.h
similarity index 59%
rename from wlr-layer-shell-client.h
rename to pkg/wayland/wlr-layer-shell-client.h
index 847e201..231c34a 100644
--- a/wlr-layer-shell-client.h
+++ b/pkg/wayland/wlr-layer-shell-client.h
@@ -3,18 +3,20 @@
 #ifndef WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
 #define WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
 
-#include 
-#include 
 #include "wayland-client.h"
+#include 
+#include 
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
- * @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1 protocol
+ * @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1
+ * protocol
  * @section page_ifaces_wlr_layer_shell_unstable_v1 Interfaces
- * - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers of the desktop
+ * - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers
+ * of the desktop
  * - @subpage page_iface_zwlr_layer_surface_v1 - layer metadata interface
  * @section page_copyright_wlr_layer_shell_unstable_v1 Copyright
  * 
@@ -127,18 +129,18 @@ extern const struct wl_interface zwlr_layer_surface_v1_interface;
 #ifndef ZWLR_LAYER_SHELL_V1_ERROR_ENUM
 #define ZWLR_LAYER_SHELL_V1_ERROR_ENUM
 enum zwlr_layer_shell_v1_error {
-	/**
-	 * wl_surface has another role
-	 */
-	ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0,
-	/**
-	 * layer value is invalid
-	 */
-	ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1,
-	/**
-	 * wl_surface has a buffer attached or committed
-	 */
-	ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2,
+  /**
+   * wl_surface has another role
+   */
+  ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0,
+  /**
+   * layer value is invalid
+   */
+  ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1,
+  /**
+   * wl_surface has a buffer attached or committed
+   */
+  ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2,
 };
 #endif /* ZWLR_LAYER_SHELL_V1_ERROR_ENUM */
 
@@ -156,17 +158,16 @@ enum zwlr_layer_shell_v1_error {
  * single layer is undefined.
  */
 enum zwlr_layer_shell_v1_layer {
-	ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
-	ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
-	ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
-	ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
+  ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
+  ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
+  ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
+  ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
 };
 #endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
 
 #define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE 0
 #define ZWLR_LAYER_SHELL_V1_DESTROY 1
 
-
 /**
  * @ingroup iface_zwlr_layer_shell_v1
  */
@@ -177,23 +178,20 @@ enum zwlr_layer_shell_v1_layer {
 #define ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION 3
 
 /** @ingroup iface_zwlr_layer_shell_v1 */
-static inline void
-zwlr_layer_shell_v1_set_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_shell_v1, user_data);
+static inline void zwlr_layer_shell_v1_set_user_data(
+    struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, void *user_data) {
+  wl_proxy_set_user_data((struct wl_proxy *)zwlr_layer_shell_v1, user_data);
 }
 
 /** @ingroup iface_zwlr_layer_shell_v1 */
-static inline void *
-zwlr_layer_shell_v1_get_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_shell_v1);
+static inline void *zwlr_layer_shell_v1_get_user_data(
+    struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) {
+  return wl_proxy_get_user_data((struct wl_proxy *)zwlr_layer_shell_v1);
 }
 
-static inline uint32_t
-zwlr_layer_shell_v1_get_version(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1);
+static inline uint32_t zwlr_layer_shell_v1_get_version(
+    struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) {
+  return wl_proxy_get_version((struct wl_proxy *)zwlr_layer_shell_v1);
 }
 
 /**
@@ -222,14 +220,18 @@ zwlr_layer_shell_v1_get_version(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
  * surface.
  */
 static inline struct zwlr_layer_surface_v1 *
-zwlr_layer_shell_v1_get_layer_surface(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, struct wl_surface *surface, struct wl_output *output, uint32_t layer, const char *namespace)
-{
-	struct wl_proxy *id;
-
-	id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1,
-			 ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE, &zwlr_layer_surface_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), 0, NULL, surface, output, layer, namespace);
-
-	return (struct zwlr_layer_surface_v1 *) id;
+zwlr_layer_shell_v1_get_layer_surface(
+    struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, struct wl_surface *surface,
+    struct wl_output *output, uint32_t layer, const char *namespace) {
+  struct wl_proxy *id;
+
+  id = wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_shell_v1,
+      ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE, &zwlr_layer_surface_v1_interface,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_shell_v1), 0, NULL,
+      surface, output, layer, namespace);
+
+  return (struct zwlr_layer_surface_v1 *)id;
 }
 
 /**
@@ -240,10 +242,11 @@ zwlr_layer_shell_v1_get_layer_surface(struct zwlr_layer_shell_v1 *zwlr_layer_she
  * are not affected.
  */
 static inline void
-zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1,
-			 ZWLR_LAYER_SHELL_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), WL_MARSHAL_FLAG_DESTROY);
+zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_shell_v1, ZWLR_LAYER_SHELL_V1_DESTROY, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_shell_v1),
+      WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM
@@ -259,66 +262,66 @@ zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
  * keyboard focus.
  */
 enum zwlr_layer_surface_v1_keyboard_interactivity {
-	/**
-	 * no keyboard focus is possible
-	 *
-	 * This value indicates that this surface is not interested in
-	 * keyboard events and the compositor should never assign it the
-	 * keyboard focus.
-	 *
-	 * This is the default value, set for newly created layer shell
-	 * surfaces.
-	 *
-	 * This is useful for e.g. desktop widgets that display information
-	 * or only have interaction with non-keyboard input devices.
-	 */
-	ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE = 0,
-	/**
-	 * request exclusive keyboard focus
-	 *
-	 * Request exclusive keyboard focus if this surface is above the
-	 * shell surface layer.
-	 *
-	 * For the top and overlay layers, the seat will always give
-	 * exclusive keyboard focus to the top-most layer which has
-	 * keyboard interactivity set to exclusive. If this layer contains
-	 * multiple surfaces with keyboard interactivity set to exclusive,
-	 * the compositor determines the one receiving keyboard events in
-	 * an implementation- defined manner. In this case, no guarantee is
-	 * made when this surface will receive keyboard focus (if ever).
-	 *
-	 * For the bottom and background layers, the compositor is allowed
-	 * to use normal focus semantics.
-	 *
-	 * This setting is mainly intended for applications that need to
-	 * ensure they receive all keyboard events, such as a lock screen
-	 * or a password prompt.
-	 */
-	ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE = 1,
-	/**
-	 * request regular keyboard focus semantics
-	 *
-	 * This requests the compositor to allow this surface to be
-	 * focused and unfocused by the user in an implementation-defined
-	 * manner. The user should be able to unfocus this surface even
-	 * regardless of the layer it is on.
-	 *
-	 * Typically, the compositor will want to use its normal mechanism
-	 * to manage keyboard focus between layer shell surfaces with this
-	 * setting and regular toplevels on the desktop layer (e.g. click
-	 * to focus). Nevertheless, it is possible for a compositor to
-	 * require a special interaction to focus or unfocus layer shell
-	 * surfaces (e.g. requiring a click even if focus follows the mouse
-	 * normally, or providing a keybinding to switch focus between
-	 * layers).
-	 *
-	 * This setting is mainly intended for desktop shell components
-	 * (e.g. panels) that allow keyboard interaction. Using this option
-	 * can allow implementing a desktop shell that can be fully usable
-	 * without the mouse.
-	 * @since 4
-	 */
-	ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND = 2,
+  /**
+   * no keyboard focus is possible
+   *
+   * This value indicates that this surface is not interested in
+   * keyboard events and the compositor should never assign it the
+   * keyboard focus.
+   *
+   * This is the default value, set for newly created layer shell
+   * surfaces.
+   *
+   * This is useful for e.g. desktop widgets that display information
+   * or only have interaction with non-keyboard input devices.
+   */
+  ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE = 0,
+  /**
+   * request exclusive keyboard focus
+   *
+   * Request exclusive keyboard focus if this surface is above the
+   * shell surface layer.
+   *
+   * For the top and overlay layers, the seat will always give
+   * exclusive keyboard focus to the top-most layer which has
+   * keyboard interactivity set to exclusive. If this layer contains
+   * multiple surfaces with keyboard interactivity set to exclusive,
+   * the compositor determines the one receiving keyboard events in
+   * an implementation- defined manner. In this case, no guarantee is
+   * made when this surface will receive keyboard focus (if ever).
+   *
+   * For the bottom and background layers, the compositor is allowed
+   * to use normal focus semantics.
+   *
+   * This setting is mainly intended for applications that need to
+   * ensure they receive all keyboard events, such as a lock screen
+   * or a password prompt.
+   */
+  ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE = 1,
+  /**
+   * request regular keyboard focus semantics
+   *
+   * This requests the compositor to allow this surface to be
+   * focused and unfocused by the user in an implementation-defined
+   * manner. The user should be able to unfocus this surface even
+   * regardless of the layer it is on.
+   *
+   * Typically, the compositor will want to use its normal mechanism
+   * to manage keyboard focus between layer shell surfaces with this
+   * setting and regular toplevels on the desktop layer (e.g. click
+   * to focus). Nevertheless, it is possible for a compositor to
+   * require a special interaction to focus or unfocus layer shell
+   * surfaces (e.g. requiring a click even if focus follows the mouse
+   * normally, or providing a keybinding to switch focus between
+   * layers).
+   *
+   * This setting is mainly intended for desktop shell components
+   * (e.g. panels) that allow keyboard interaction. Using this option
+   * can allow implementing a desktop shell that can be fully usable
+   * without the mouse.
+   * @since 4
+   */
+  ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND = 2,
 };
 /**
  * @ingroup iface_zwlr_layer_surface_v1
@@ -329,44 +332,44 @@ enum zwlr_layer_surface_v1_keyboard_interactivity {
 #ifndef ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
 #define ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
 enum zwlr_layer_surface_v1_error {
-	/**
-	 * provided surface state is invalid
-	 */
-	ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0,
-	/**
-	 * size is invalid
-	 */
-	ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1,
-	/**
-	 * anchor bitfield is invalid
-	 */
-	ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2,
-	/**
-	 * keyboard interactivity is invalid
-	 */
-	ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY = 3,
+  /**
+   * provided surface state is invalid
+   */
+  ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0,
+  /**
+   * size is invalid
+   */
+  ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1,
+  /**
+   * anchor bitfield is invalid
+   */
+  ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2,
+  /**
+   * keyboard interactivity is invalid
+   */
+  ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY = 3,
 };
 #endif /* ZWLR_LAYER_SURFACE_V1_ERROR_ENUM */
 
 #ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
 #define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
 enum zwlr_layer_surface_v1_anchor {
-	/**
-	 * the top edge of the anchor rectangle
-	 */
-	ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1,
-	/**
-	 * the bottom edge of the anchor rectangle
-	 */
-	ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2,
-	/**
-	 * the left edge of the anchor rectangle
-	 */
-	ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4,
-	/**
-	 * the right edge of the anchor rectangle
-	 */
-	ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8,
+  /**
+   * the top edge of the anchor rectangle
+   */
+  ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1,
+  /**
+   * the bottom edge of the anchor rectangle
+   */
+  ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2,
+  /**
+   * the left edge of the anchor rectangle
+   */
+  ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4,
+  /**
+   * the right edge of the anchor rectangle
+   */
+  ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8,
 };
 #endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */
 
@@ -375,59 +378,56 @@ enum zwlr_layer_surface_v1_anchor {
  * @struct zwlr_layer_surface_v1_listener
  */
 struct zwlr_layer_surface_v1_listener {
-	/**
-	 * suggest a surface change
-	 *
-	 * The configure event asks the client to resize its surface.
-	 *
-	 * Clients should arrange their surface for the new states, and
-	 * then send an ack_configure request with the serial sent in this
-	 * configure event at some point before committing the new surface.
-	 *
-	 * The client is free to dismiss all but the last configure event
-	 * it received.
-	 *
-	 * The width and height arguments specify the size of the window in
-	 * surface-local coordinates.
-	 *
-	 * The size is a hint, in the sense that the client is free to
-	 * ignore it if it doesn't resize, pick a smaller size (to satisfy
-	 * aspect ratio or resize in steps of NxM pixels). If the client
-	 * picks a smaller size and is anchored to two opposite anchors
-	 * (e.g. 'top' and 'bottom'), the surface will be centered on this
-	 * axis.
-	 *
-	 * If the width or height arguments are zero, it means the client
-	 * should decide its own window dimension.
-	 */
-	void (*configure)(void *data,
-			  struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
-			  uint32_t serial,
-			  uint32_t width,
-			  uint32_t height);
-	/**
-	 * surface should be closed
-	 *
-	 * The closed event is sent by the compositor when the surface
-	 * will no longer be shown. The output may have been destroyed or
-	 * the user may have asked for it to be removed. Further changes to
-	 * the surface will be ignored. The client should destroy the
-	 * resource after receiving this event, and create a new surface if
-	 * they so choose.
-	 */
-	void (*closed)(void *data,
-		       struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1);
+  /**
+   * suggest a surface change
+   *
+   * The configure event asks the client to resize its surface.
+   *
+   * Clients should arrange their surface for the new states, and
+   * then send an ack_configure request with the serial sent in this
+   * configure event at some point before committing the new surface.
+   *
+   * The client is free to dismiss all but the last configure event
+   * it received.
+   *
+   * The width and height arguments specify the size of the window in
+   * surface-local coordinates.
+   *
+   * The size is a hint, in the sense that the client is free to
+   * ignore it if it doesn't resize, pick a smaller size (to satisfy
+   * aspect ratio or resize in steps of NxM pixels). If the client
+   * picks a smaller size and is anchored to two opposite anchors
+   * (e.g. 'top' and 'bottom'), the surface will be centered on this
+   * axis.
+   *
+   * If the width or height arguments are zero, it means the client
+   * should decide its own window dimension.
+   */
+  void (*configure)(void *data,
+                    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
+                    uint32_t serial, uint32_t width, uint32_t height);
+  /**
+   * surface should be closed
+   *
+   * The closed event is sent by the compositor when the surface
+   * will no longer be shown. The output may have been destroyed or
+   * the user may have asked for it to be removed. Further changes to
+   * the surface will be ignored. The client should destroy the
+   * resource after receiving this event, and create a new surface if
+   * they so choose.
+   */
+  void (*closed)(void *data,
+                 struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1);
 };
 
 /**
  * @ingroup iface_zwlr_layer_surface_v1
  */
-static inline int
-zwlr_layer_surface_v1_add_listener(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
-				   const struct zwlr_layer_surface_v1_listener *listener, void *data)
-{
-	return wl_proxy_add_listener((struct wl_proxy *) zwlr_layer_surface_v1,
-				     (void (**)(void)) listener, data);
+static inline int zwlr_layer_surface_v1_add_listener(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
+    const struct zwlr_layer_surface_v1_listener *listener, void *data) {
+  return wl_proxy_add_listener((struct wl_proxy *)zwlr_layer_surface_v1,
+                               (void (**)(void))listener, data);
 }
 
 #define ZWLR_LAYER_SURFACE_V1_SET_SIZE 0
@@ -487,23 +487,20 @@ zwlr_layer_surface_v1_add_listener(struct zwlr_layer_surface_v1 *zwlr_layer_surf
 #define ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION 2
 
 /** @ingroup iface_zwlr_layer_surface_v1 */
-static inline void
-zwlr_layer_surface_v1_set_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, void *user_data)
-{
-	wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_surface_v1, user_data);
+static inline void zwlr_layer_surface_v1_set_user_data(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, void *user_data) {
+  wl_proxy_set_user_data((struct wl_proxy *)zwlr_layer_surface_v1, user_data);
 }
 
 /** @ingroup iface_zwlr_layer_surface_v1 */
-static inline void *
-zwlr_layer_surface_v1_get_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
-{
-	return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_surface_v1);
+static inline void *zwlr_layer_surface_v1_get_user_data(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) {
+  return wl_proxy_get_user_data((struct wl_proxy *)zwlr_layer_surface_v1);
 }
 
-static inline uint32_t
-zwlr_layer_surface_v1_get_version(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
-{
-	return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1);
+static inline uint32_t zwlr_layer_surface_v1_get_version(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) {
+  return wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1);
 }
 
 /**
@@ -520,11 +517,13 @@ zwlr_layer_surface_v1_get_version(struct zwlr_layer_surface_v1 *zwlr_layer_surfa
  *
  * Size is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_size(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t width, uint32_t height)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, width, height);
+static inline void zwlr_layer_surface_v1_set_size(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t width,
+    uint32_t height) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1, ZWLR_LAYER_SURFACE_V1_SET_SIZE,
+      NULL, wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      width, height);
 }
 
 /**
@@ -538,11 +537,13 @@ zwlr_layer_surface_v1_set_size(struct zwlr_layer_surface_v1 *zwlr_layer_surface_
  *
  * Anchor is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_anchor(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t anchor)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, anchor);
+static inline void zwlr_layer_surface_v1_set_anchor(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t anchor) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1,
+      ZWLR_LAYER_SURFACE_V1_SET_ANCHOR, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      anchor);
 }
 
 /**
@@ -581,11 +582,12 @@ zwlr_layer_surface_v1_set_anchor(struct zwlr_layer_surface_v1 *zwlr_layer_surfac
  *
  * Exclusive zone is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_exclusive_zone(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t zone)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, zone);
+static inline void zwlr_layer_surface_v1_set_exclusive_zone(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t zone) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1,
+      ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0, zone);
 }
 
 /**
@@ -599,11 +601,14 @@ zwlr_layer_surface_v1_set_exclusive_zone(struct zwlr_layer_surface_v1 *zwlr_laye
  *
  * Margin is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_margin(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t top, int32_t right, int32_t bottom, int32_t left)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_MARGIN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, top, right, bottom, left);
+static inline void zwlr_layer_surface_v1_set_margin(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t top,
+    int32_t right, int32_t bottom, int32_t left) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1,
+      ZWLR_LAYER_SURFACE_V1_SET_MARGIN, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0, top,
+      right, bottom, left);
 }
 
 /**
@@ -622,11 +627,14 @@ zwlr_layer_surface_v1_set_margin(struct zwlr_layer_surface_v1 *zwlr_layer_surfac
  *
  * Keyboard interactivity is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_keyboard_interactivity(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t keyboard_interactivity)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, keyboard_interactivity);
+static inline void zwlr_layer_surface_v1_set_keyboard_interactivity(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
+    uint32_t keyboard_interactivity) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1,
+      ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      keyboard_interactivity);
 }
 
 /**
@@ -640,11 +648,13 @@ zwlr_layer_surface_v1_set_keyboard_interactivity(struct zwlr_layer_surface_v1 *z
  * See the documentation of xdg_popup for more details about what an
  * xdg_popup is and how it is used.
  */
-static inline void
-zwlr_layer_surface_v1_get_popup(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, struct xdg_popup *popup)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_GET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, popup);
+static inline void zwlr_layer_surface_v1_get_popup(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
+    struct xdg_popup *popup) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1, ZWLR_LAYER_SURFACE_V1_GET_POPUP,
+      NULL, wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      popup);
 }
 
 /**
@@ -666,11 +676,13 @@ zwlr_layer_surface_v1_get_popup(struct zwlr_layer_surface_v1 *zwlr_layer_surface
  * only the last request sent before a commit indicates which configure
  * event the client really is responding to.
  */
-static inline void
-zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, serial);
+static inline void zwlr_layer_surface_v1_ack_configure(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1,
+      ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE, NULL,
+      wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      serial);
 }
 
 /**
@@ -678,11 +690,12 @@ zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *zwlr_layer_sur
  *
  * This request destroys the layer surface.
  */
-static inline void
-zwlr_layer_surface_v1_destroy(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), WL_MARSHAL_FLAG_DESTROY);
+static inline void zwlr_layer_surface_v1_destroy(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1, ZWLR_LAYER_SURFACE_V1_DESTROY,
+      NULL, wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1),
+      WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -692,14 +705,15 @@ zwlr_layer_surface_v1_destroy(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v
  *
  * Layer is double-buffered, see wl_surface.commit.
  */
-static inline void
-zwlr_layer_surface_v1_set_layer(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t layer)
-{
-	wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
-			 ZWLR_LAYER_SURFACE_V1_SET_LAYER, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, layer);
+static inline void zwlr_layer_surface_v1_set_layer(
+    struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t layer) {
+  wl_proxy_marshal_flags(
+      (struct wl_proxy *)zwlr_layer_surface_v1, ZWLR_LAYER_SURFACE_V1_SET_LAYER,
+      NULL, wl_proxy_get_version((struct wl_proxy *)zwlr_layer_surface_v1), 0,
+      layer);
 }
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 }
 #endif
 
diff --git a/wayland.go b/wayland.go
deleted file mode 100644
index 9dac652..0000000
--- a/wayland.go
+++ /dev/null
@@ -1,590 +0,0 @@
-package main
-
-/*
-#cgo pkg-config: wayland-client wayland-egl egl gl
-#cgo LDFLAGS: -lwayland-client -lwayland-egl -lEGL -lGL
-#cgo CFLAGS: -I.
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include "wlr-layer-shell-client.h"
-#include "keyboard-shortcuts-inhibit-client.h"
-
-#include 
-#include 
-
-#ifndef __has_attribute
-# define __has_attribute(x) 0
-#endif
-
-#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
-#define WL_PRIVATE __attribute__ ((visibility("hidden")))
-#else
-#define WL_PRIVATE
-#endif
-
-extern const struct wl_interface wl_output_interface;
-extern const struct wl_interface wl_surface_interface;
-extern const struct wl_interface zwlr_layer_surface_v1_interface;
-
-static const struct wl_interface xdg_popup_interface = {
-	"xdg_popup", 0, 0, NULL, 0, NULL,
-};
-
-static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = {
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	&zwlr_layer_surface_v1_interface,
-	&wl_surface_interface,
-	&wl_output_interface,
-	NULL,
-	NULL,
-	&xdg_popup_interface,
-};
-
-static const struct wl_message zwlr_layer_shell_v1_requests[] = {
-	{ "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 },
-	{ "destroy", "3", wlr_layer_shell_unstable_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = {
-	"zwlr_layer_shell_v1", 4,
-	2, zwlr_layer_shell_v1_requests,
-	0, NULL,
-};
-
-static const struct wl_message zwlr_layer_surface_v1_requests[] = {
-	{ "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 },
-	{ "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "destroy", "", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0 },
-};
-
-static const struct wl_message zwlr_layer_surface_v1_events[] = {
-	{ "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 },
-	{ "closed", "", wlr_layer_shell_unstable_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = {
-	"zwlr_layer_surface_v1", 4,
-	9, zwlr_layer_surface_v1_requests,
-	2, zwlr_layer_surface_v1_events,
-};
-
-static const struct wl_interface *keyboard_shortcuts_inhibit_unstable_v1_types[] = {
-	&zwp_keyboard_shortcuts_inhibitor_v1_interface,
-	&wl_surface_interface,
-	&wl_seat_interface,
-};
-
-static const struct wl_message zwp_keyboard_shortcuts_inhibit_manager_v1_requests[] = {
-	{ "destroy", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0 },
-	{ "inhibit_shortcuts", "noo", keyboard_shortcuts_inhibit_unstable_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface zwp_keyboard_shortcuts_inhibit_manager_v1_interface = {
-	"zwp_keyboard_shortcuts_inhibit_manager_v1", 1,
-	2, zwp_keyboard_shortcuts_inhibit_manager_v1_requests,
-	0, NULL,
-};
-
-static const struct wl_message zwp_keyboard_shortcuts_inhibitor_v1_requests[] = {
-	{ "destroy", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0 },
-};
-
-static const struct wl_message zwp_keyboard_shortcuts_inhibitor_v1_events[] = {
-	{ "active", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0 },
-	{ "inactive", "", keyboard_shortcuts_inhibit_unstable_v1_types + 0 },
-};
-
-WL_PRIVATE const struct wl_interface zwp_keyboard_shortcuts_inhibitor_v1_interface = {
-	"zwp_keyboard_shortcuts_inhibitor_v1", 1,
-	1, zwp_keyboard_shortcuts_inhibitor_v1_requests,
-	2, zwp_keyboard_shortcuts_inhibitor_v1_events,
-};
-
-struct wl_compositor *compositor = NULL;
-struct zwlr_layer_shell_v1 *layer_shell = NULL;
-struct wl_seat *seat = NULL;
-struct wl_pointer *pointer = NULL;
-struct wl_keyboard *keyboard = NULL;
-struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager = NULL;
-struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibitor = NULL;
-struct zwlr_layer_surface_v1 *layer_surface_global = NULL;
-int32_t width_global = 0;
-int32_t height_global = 0;
-
-void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
-                             uint32_t serial, uint32_t width, uint32_t height) {
-    width_global = width;
-    height_global = height;
-    zwlr_layer_surface_v1_ack_configure(surface, serial);
-}
-
-void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) {
-}
-
-static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
-    .configure = layer_surface_configure,
-    .closed = layer_surface_closed,
-};
-
-// Forward declarations for seat
-void seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities);
-void seat_name(void *data, struct wl_seat *seat, const char *name);
-
-static const struct wl_seat_listener seat_listener = {
-    .capabilities = seat_capabilities,
-    .name = seat_name,
-};
-
-static void registry_global(void *data, struct wl_registry *registry,
-                           uint32_t name, const char *interface,
-                           uint32_t version) {
-    if (strcmp(interface, "wl_compositor") == 0) {
-        compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
-    } else if (strcmp(interface, "zwlr_layer_shell_v1") == 0) {
-        layer_shell = (struct zwlr_layer_shell_v1 *)
-            wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
-    } else if (strcmp(interface, "wl_seat") == 0) {
-        seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
-        wl_seat_add_listener(seat, &seat_listener, NULL);
-    } else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {
-        shortcuts_inhibit_manager = (struct zwp_keyboard_shortcuts_inhibit_manager_v1 *)
-            wl_registry_bind(registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
-    }
-}
-
-static void registry_global_remove(void *data, struct wl_registry *registry,
-                                   uint32_t name) {
-}
-
-static const struct wl_registry_listener registry_listener = {
-    .global = registry_global,
-    .global_remove = registry_global_remove,
-};
-
-struct wl_registry *get_registry(struct wl_display *display) {
-    return wl_display_get_registry(display);
-}
-
-void add_registry_listener(struct wl_registry *registry) {
-    wl_registry_add_listener(registry, ®istry_listener, NULL);
-}
-
-struct wl_surface *surface_global = NULL;
-
-struct zwlr_layer_surface_v1 *create_layer_surface(struct wl_surface *surface) {
-    surface_global = surface;
-
-    layer_surface_global =
-        zwlr_layer_shell_v1_get_layer_surface(
-            layer_shell, surface, NULL,
-            ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "hexecute");
-
-    zwlr_layer_surface_v1_set_anchor(layer_surface_global,
-        ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
-        ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
-        ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
-        ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
-
-    zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_global, -1);
-
-    zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_global,
-        ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE);
-
-    zwlr_layer_surface_v1_add_listener(layer_surface_global, &layer_surface_listener, NULL);
-
-    wl_surface_commit(surface);
-
-    return layer_surface_global;
-}
-
-void set_input_region(int32_t width, int32_t height) {
-    if (surface_global) {
-        struct wl_region *region = wl_compositor_create_region(compositor);
-        wl_region_add(region, 0, 0, width, height);
-        wl_surface_set_input_region(surface_global, region);
-        wl_region_destroy(region);
-        wl_surface_commit(surface_global);
-    }
-}
-
-void disable_all_input() {
-    if (shortcuts_inhibitor) {
-        zwp_keyboard_shortcuts_inhibitor_v1_destroy(shortcuts_inhibitor);
-        shortcuts_inhibitor = NULL;
-    }
-
-    if (layer_surface_global) {
-        zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_global,
-            ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE);
-    }
-
-    if (surface_global) {
-        struct wl_region *region = wl_compositor_create_region(compositor);
-        wl_surface_set_input_region(surface_global, region);
-        wl_region_destroy(region);
-        wl_surface_commit(surface_global);
-    }
-}
-
-static int button_state = 0;
-static double mouse_x = 0;
-static double mouse_y = 0;
-
-void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
-                  struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) {
-    mouse_x = wl_fixed_to_double(x);
-    mouse_y = wl_fixed_to_double(y);
-    wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
-}
-
-void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial,
-                  struct wl_surface *surface) {
-}
-
-void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time,
-                   wl_fixed_t x, wl_fixed_t y) {
-    mouse_x = wl_fixed_to_double(x);
-    mouse_y = wl_fixed_to_double(y);
-}
-
-void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial,
-                   uint32_t time, uint32_t button, uint32_t state) {
-    if (button == 272) {
-        button_state = state;
-    }
-}
-
-void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time,
-                 uint32_t axis, wl_fixed_t value) {
-}
-
-void pointer_frame(void *data, struct wl_pointer *pointer) {
-}
-
-void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t source) {
-}
-
-void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) {
-}
-
-void pointer_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) {
-}
-
-static const struct wl_pointer_listener pointer_listener = {
-    .enter = pointer_enter,
-    .leave = pointer_leave,
-    .motion = pointer_motion,
-    .button = pointer_button,
-    .axis = pointer_axis,
-    .frame = pointer_frame,
-    .axis_source = pointer_axis_source,
-    .axis_stop = pointer_axis_stop,
-    .axis_discrete = pointer_axis_discrete,
-};
-
-static uint32_t last_key = 0;
-static uint32_t last_key_state = 0;
-
-void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
-                     int32_t fd, uint32_t size) {
-    close(fd);
-}
-
-void keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,
-                    struct wl_surface *surface, struct wl_array *keys) {
-}
-
-void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,
-                    struct wl_surface *surface) {
-}
-
-void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
-                  uint32_t time, uint32_t key, uint32_t state) {
-    last_key = key;
-    last_key_state = state;
-}
-
-void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial,
-                        uint32_t mods_depressed, uint32_t mods_latched,
-                        uint32_t mods_locked, uint32_t group) {
-}
-
-void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard,
-                          int32_t rate, int32_t delay) {
-}
-
-static const struct wl_keyboard_listener keyboard_listener = {
-    .keymap = keyboard_keymap,
-    .enter = keyboard_enter,
-    .leave = keyboard_leave,
-    .key = keyboard_key,
-    .modifiers = keyboard_modifiers,
-    .repeat_info = keyboard_repeat_info,
-};
-
-// Seat listener
-void seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities) {
-    if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
-        pointer = wl_seat_get_pointer(seat);
-        wl_pointer_add_listener(pointer, &pointer_listener, NULL);
-    }
-
-    if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
-        keyboard = wl_seat_get_keyboard(seat);
-        wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
-
-        if (shortcuts_inhibit_manager && surface_global && !shortcuts_inhibitor) {
-            shortcuts_inhibitor = zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
-                shortcuts_inhibit_manager, surface_global, seat);
-        }
-    }
-}
-
-void seat_name(void *data, struct wl_seat *seat, const char *name) {
-}
-
-int get_button_state() {
-    return button_state;
-}
-
-void get_mouse_pos(double *x, double *y) {
-    *x = mouse_x;
-    *y = mouse_y;
-}
-
-void get_dimensions(int32_t *w, int32_t *h) {
-    *w = width_global;
-    *h = height_global;
-}
-
-uint32_t get_last_key() {
-    return last_key;
-}
-
-uint32_t get_last_key_state() {
-    return last_key_state;
-}
-
-void clear_last_key() {
-    last_key = 0;
-    last_key_state = 0;
-}
-*/
-import "C"
-import (
-	"fmt"
-)
-
-type WaylandError struct {
-	msg string
-}
-
-func (e *WaylandError) Error() string {
-	return e.msg
-}
-
-type WaylandWindow struct {
-	display       *C.struct_wl_display
-	registry      *C.struct_wl_registry
-	surface       *C.struct_wl_surface
-	layerSurface  *C.struct_zwlr_layer_surface_v1
-	eglWindow     *C.struct_wl_egl_window
-	eglDisplay    C.EGLDisplay
-	eglContext    C.EGLContext
-	eglSurface    C.EGLSurface
-	width, height int32
-}
-
-func NewWaylandWindow() (*WaylandWindow, error) {
-	w := &WaylandWindow{}
-
-	w.display = C.wl_display_connect(nil)
-	if w.display == nil {
-		return nil, &WaylandError{"failed to connect to Wayland display"}
-	}
-
-	w.registry = C.get_registry(w.display)
-	C.add_registry_listener(w.registry)
-	C.wl_display_roundtrip(w.display)
-	if C.compositor == nil {
-		return nil, &WaylandError{"compositor not available"}
-	}
-	if C.layer_shell == nil {
-		return nil, &WaylandError{"layer shell not available"}
-	}
-
-	w.surface = C.wl_compositor_create_surface(C.compositor)
-	if w.surface == nil {
-		return nil, &WaylandError{"failed to create surface"}
-	}
-
-	w.layerSurface = C.create_layer_surface(w.surface)
-
-	C.wl_display_roundtrip(w.display)
-
-	var width, height C.int32_t
-	C.get_dimensions(&width, &height)
-	w.width = int32(width)
-	w.height = int32(height)
-
-	if w.width == 0 || w.height == 0 {
-		w.width = 1920
-		w.height = 1080
-	}
-
-	C.wl_display_roundtrip(w.display)
-
-	C.set_input_region(C.int32_t(w.width), C.int32_t(w.height))
-
-	if err := w.initEGL(); err != nil {
-		return nil, err
-	}
-
-	C.wl_surface_commit(w.surface)
-	C.wl_display_flush(w.display)
-
-	C.wl_display_roundtrip(w.display)
-	C.wl_display_roundtrip(w.display)
-	C.wl_display_flush(w.display)
-
-	return w, nil
-}
-
-func (w *WaylandWindow) initEGL() error {
-	w.eglWindow = C.wl_egl_window_create(w.surface, C.int(w.width), C.int(w.height))
-	if w.eglWindow == nil {
-		return fmt.Errorf("failed to create EGL window")
-	}
-
-	w.eglDisplay = C.eglGetDisplay(C.EGLNativeDisplayType(w.display))
-	if w.eglDisplay == C.EGLDisplay(C.EGL_NO_DISPLAY) {
-		return fmt.Errorf("failed to get EGL display")
-	}
-
-	var major, minor C.EGLint
-	if C.eglInitialize(w.eglDisplay, &major, &minor) == C.EGL_FALSE {
-		return fmt.Errorf("failed to initialize EGL")
-	}
-
-	configAttribs := []C.EGLint{
-		C.EGL_SURFACE_TYPE, C.EGL_WINDOW_BIT,
-		C.EGL_RED_SIZE, 8,
-		C.EGL_GREEN_SIZE, 8,
-		C.EGL_BLUE_SIZE, 8,
-		C.EGL_ALPHA_SIZE, 8,
-		C.EGL_RENDERABLE_TYPE, C.EGL_OPENGL_BIT,
-		C.EGL_NONE,
-	}
-
-	var config C.EGLConfig
-	var numConfigs C.EGLint
-	if C.eglChooseConfig(w.eglDisplay, &configAttribs[0], &config, 1, &numConfigs) == C.EGL_FALSE {
-		return fmt.Errorf("failed to choose EGL config")
-	}
-
-	C.eglBindAPI(C.EGL_OPENGL_API)
-	contextAttribs := []C.EGLint{
-		C.EGL_CONTEXT_MAJOR_VERSION, 4,
-		C.EGL_CONTEXT_MINOR_VERSION, 1,
-		C.EGL_CONTEXT_OPENGL_PROFILE_MASK, C.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
-		C.EGL_NONE,
-	}
-
-	w.eglContext = C.eglCreateContext(w.eglDisplay, config, nil, &contextAttribs[0])
-	if w.eglContext == nil {
-		return fmt.Errorf("failed to create EGL context")
-	}
-
-	w.eglSurface = C.eglCreateWindowSurface(w.eglDisplay, config, C.EGLNativeWindowType(w.eglWindow), nil)
-	if w.eglSurface == nil {
-		return fmt.Errorf("failed to create EGL surface")
-	}
-
-	if C.eglMakeCurrent(w.eglDisplay, w.eglSurface, w.eglSurface, w.eglContext) == C.EGL_FALSE {
-		return fmt.Errorf("failed to make EGL context current")
-	}
-
-	return nil
-}
-
-func (w *WaylandWindow) GetSize() (int, int) {
-	var width, height C.int32_t
-	C.get_dimensions(&width, &height)
-	if width > 0 && height > 0 {
-		w.width = int32(width)
-		w.height = int32(height)
-	}
-	return int(w.width), int(w.height)
-}
-
-func (w *WaylandWindow) ShouldClose() bool {
-	return false
-}
-
-func (w *WaylandWindow) SwapBuffers() {
-	C.eglSwapBuffers(w.eglDisplay, w.eglSurface)
-}
-
-func (w *WaylandWindow) PollEvents() {
-	C.wl_display_flush(w.display)
-	C.wl_display_dispatch_pending(w.display)
-}
-
-func (w *WaylandWindow) GetCursorPos() (float64, float64) {
-	var x, y C.double
-	C.get_mouse_pos(&x, &y)
-	return float64(x), float64(y)
-}
-
-func (w *WaylandWindow) GetMouseButton() bool {
-	state := C.get_button_state()
-	return state == 1
-}
-
-func (w *WaylandWindow) DisableInput() {
-	C.disable_all_input()
-}
-
-func (w *WaylandWindow) GetLastKey() (uint32, uint32, bool) {
-	key := uint32(C.get_last_key())
-	state := uint32(C.get_last_key_state())
-	return key, state, key != 0
-}
-
-func (w *WaylandWindow) ClearLastKey() {
-	C.clear_last_key()
-}
-
-func (w *WaylandWindow) Destroy() {
-	if w.eglContext != C.EGLContext(C.EGL_NO_CONTEXT) {
-		C.eglDestroyContext(w.eglDisplay, w.eglContext)
-	}
-	if w.eglSurface != C.EGLSurface(C.EGL_NO_SURFACE) {
-		C.eglDestroySurface(w.eglDisplay, w.eglSurface)
-	}
-	if w.eglWindow != nil {
-		C.wl_egl_window_destroy(w.eglWindow)
-	}
-	if w.eglDisplay != C.EGLDisplay(C.EGL_NO_DISPLAY) {
-		C.eglTerminate(w.eglDisplay)
-	}
-	if w.surface != nil {
-		C.wl_surface_destroy(w.surface)
-	}
-	if w.display != nil {
-		C.wl_display_disconnect(w.display)
-	}
-}