Skip to content

Commit

Permalink
Merge pull request #16 from thiagokokada/import-event-from-hyprland-i…
Browse files Browse the repository at this point in the history
…pc-client

Import event from hyprland-ipc-client
  • Loading branch information
thiagokokada authored Aug 31, 2024
2 parents 408707d + 9d3c4e0 commit bf027a2
Show file tree
Hide file tree
Showing 12 changed files with 528 additions and 133 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2024 Thiago Kenji Okada
Copyright (c) 2024 labi-le

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,9 @@ library.
for general usage, sending commands directly to the IPC socket of Hyprland is
supported for i.e.: performance, e.g.: `c.RawRequest("[[BATCH]] dispatch exec
kitty, keyword general:border_size 1")`

## Planned

- [Events](https://wiki.hyprland.org/Plugins/Development/Event-list/)
- [Events:](https://wiki.hyprland.org/Plugins/Development/Event-list/) to
subscribe and handle Hyprland events, see
[events](./examples/events/events.go) for an example on how to use it.

## Development

Expand Down
67 changes: 0 additions & 67 deletions event.go

This file was deleted.

171 changes: 171 additions & 0 deletions event/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package event

import (
"bufio"
"fmt"
"net"
"strings"

"github.com/thiagokokada/hyprland-go/internal/assert"
"github.com/thiagokokada/hyprland-go/internal/helpers"
)

const (
bufSize = 8192
sep = ">>"
)

// Initiate a new client or panic.
// This should be the preferred method for user scripts, since it will
// automatically find the proper socket to connect and use the
// HYPRLAND_INSTANCE_SIGNATURE for the current user.
// If you need to connect to arbitrary user instances or need a method that
// will not panic on error, use [NewEventClient] instead.
func MustEventClient() *EventClient {
return assert.Must1(NewEventClient(helpers.MustSocket(".socket2.sock")))
}

// Initiate a new event client.
// Receive as parameters a socket that is generally localised in
// '$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock'.
func NewEventClient(socket string) (*EventClient, error) {
conn, err := net.Dial("unix", socket)
if err != nil {
return nil, fmt.Errorf("error while connecting to socket: %w", err)
}
return &EventClient{conn: conn}, nil
}

// Low-level receive event method, should be avoided unless there is no
// alternative.
func (c *EventClient) Receive() ([]ReceivedData, error) {
buf := make([]byte, bufSize)
reader := bufio.NewReader(c.conn)
n, err := reader.Read(buf)
if err != nil {
return nil, err
}

buf = buf[:n]

var recv []ReceivedData
raw := strings.Split(string(buf), "\n")
for _, event := range raw {
if event == "" {
continue
}

split := strings.Split(event, sep)
if split[0] == "" || split[1] == "" || split[1] == "," {
continue
}

recv = append(recv, ReceivedData{
Type: EventType(split[0]),
Data: RawData(split[1]),
})
}

return recv, nil
}

// Subscribe to events.
// You need to pass an implementation of [EventHandler] interface for each of
// the events you want to handle and all event types you want to handle.
func Subscribe(c *EventClient, ev EventHandler, events ...EventType) error {
for {
msg, err := c.Receive()
if err != nil {
return err
}

for _, data := range msg {
processEvent(ev, data, events)
}
}
}

func processEvent(ev EventHandler, msg ReceivedData, events []EventType) {
for _, event := range events {
raw := strings.Split(string(msg.Data), ",")
if msg.Type == event {
switch event {
case EventWorkspace:
// e.g. "1" (workspace number)
ev.Workspace(WorkspaceName(raw[0]))
case EventFocusedMonitor:
// idk
ev.FocusedMonitor(FocusedMonitor{
MonitorName: MonitorName(raw[0]),
WorkspaceName: WorkspaceName(raw[1]),
})
case EventActiveWindow:
// e.g. jetbrains-goland,hyprland-ipc-ipc – main.go
ev.ActiveWindow(ActiveWindow{
Name: raw[0],
Title: raw[1],
})
case EventFullscreen:
// e.g. "true" or "false"
ev.Fullscreen(raw[0] == "1")
case EventMonitorRemoved:
// e.g. idk
ev.MonitorRemoved(MonitorName(raw[0]))
case EventMonitorAdded:
// e.g. idk
ev.MonitorAdded(MonitorName(raw[0]))
case EventCreateWorkspace:
// e.g. "1" (workspace number)
ev.CreateWorkspace(WorkspaceName(raw[0]))
case EventDestroyWorkspace:
// e.g. "1" (workspace number)
ev.DestroyWorkspace(WorkspaceName(raw[0]))
case EventMoveWorkspace:
// e.g. idk
ev.MoveWorkspace(MoveWorkspace{
WorkspaceName: WorkspaceName(raw[0]),
MonitorName: MonitorName(raw[1]),
})
case EventActiveLayout:
// e.g. AT Translated Set 2 keyboard,Russian
ev.ActiveLayout(ActiveLayout{
Type: raw[0],
Name: raw[1],
})
case EventOpenWindow:
// e.g. 80864f60,1,Alacritty,Alacritty
ev.OpenWindow(OpenWindow{
Address: raw[0],
WorkspaceName: WorkspaceName(raw[1]),
Class: raw[2],
Title: raw[3],
})
case EventCloseWindow:
// e.g. 5
ev.CloseWindow(CloseWindow{
Address: raw[0],
})
case EventMoveWindow:
// e.g. 5
ev.MoveWindow(MoveWindow{
Address: raw[0],
WorkspaceName: WorkspaceName(raw[1]),
})
case EventOpenLayer:
// e.g. wofi
ev.OpenLayer(OpenLayer(raw[0]))
case EventCloseLayer:
// e.g. wofi
ev.CloseLayer(CloseLayer(raw[0]))
case EventSubMap:
// e.g. idk
ev.SubMap(SubMap(raw[0]))
case EventScreencast:
ev.Screencast(Screencast{
Sharing: raw[0] == "1",
Owner: raw[1],
})
}
}
}
}
24 changes: 24 additions & 0 deletions event/event_noop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package event

// NoopEventHandler is an implementation of [EventHandler] interface with all
// methods doing nothing. It is a good starting point to be embedded your own
// struct to be extended.
type NoopEventHandler struct{}

func (e *NoopEventHandler) Workspace(WorkspaceName) {}
func (e *NoopEventHandler) FocusedMonitor(FocusedMonitor) {}
func (e *NoopEventHandler) ActiveWindow(ActiveWindow) {}
func (e *NoopEventHandler) Fullscreen(bool) {}
func (e *NoopEventHandler) MonitorRemoved(MonitorName) {}
func (e *NoopEventHandler) MonitorAdded(MonitorName) {}
func (e *NoopEventHandler) CreateWorkspace(WorkspaceName) {}
func (e *NoopEventHandler) DestroyWorkspace(WorkspaceName) {}
func (e *NoopEventHandler) MoveWorkspace(MoveWorkspace) {}
func (e *NoopEventHandler) ActiveLayout(ActiveLayout) {}
func (e *NoopEventHandler) OpenWindow(OpenWindow) {}
func (e *NoopEventHandler) CloseWindow(CloseWindow) {}
func (e *NoopEventHandler) MoveWindow(MoveWindow) {}
func (e *NoopEventHandler) OpenLayer(OpenLayer) {}
func (e *NoopEventHandler) CloseLayer(CloseLayer) {}
func (e *NoopEventHandler) SubMap(SubMap) {}
func (e *NoopEventHandler) Screencast(Screencast) {}
Loading

0 comments on commit bf027a2

Please sign in to comment.