Compare commits

...

24 Commits
phoen ... main

Author SHA1 Message Date
Daniel Barlow c485e59c3a explain how to start eufon on ssh connection 2022-08-14 14:45:41 +00:00
Daniel Barlow a19eb2c8de Merge branch 'main' of gti.telent.net:dan/eufon 2022-08-14 14:07:49 +00:00
Daniel Barlow 771d80c70d stuff 2022-08-14 14:07:29 +00:00
Daniel Barlow 32e78a24db fudging to make it work as buildable derivation
a bit too much path shenanigans going on here to be entirely happy
2022-08-14 15:05:05 +01:00
Daniel Barlow 4c4bd9e997 make an installable derivation
untested as yet
2022-08-14 10:48:18 +00:00
Daniel Barlow cba4549304 update README to describe configuration.nix 2022-08-02 23:24:05 +00:00
Daniel Barlow 7dac09a17a emacs magic to use eufonctl as inferior lisp 2022-07-12 23:16:45 +00:00
Daniel Barlow 2975908e81 kiwmi verbose log and sensible syslog identifier 2022-07-12 23:16:45 +00:00
Daniel Barlow 5a5e1de965 add eufonctl script 2022-07-12 23:16:45 +00:00
Daniel Barlow c980f7fe20 initial support for touch events
Calls lua handlers, then passes the event to the appropriate client

Doesn't do pointer emulation (unlike sway) because in the
GNOME/Gtk worldview that's done client-side, so my assumption is
anything else that wants to co-exist with GNOME also has to
do it client-side

Still to do:

- verify whether return value of the lua handler will affect whether
event passed to child

- clean up the hacky int we use to track whether to set touch
seat capability
2022-07-12 23:16:44 +00:00
Daniel Barlow 2c51676436 choose view by clicking it in overview 2022-07-07 12:17:46 +01:00
Daniel Barlow 36496ad126 pointer focus needs to account for view transform matrix 2022-07-07 11:58:40 +01:00
Daniel Barlow 41b72f95c1 hook up overview button, first part
- co-ordinates hardcoded, need to adjust for screen size
- confuses the pointer x/y mapping for underlying views
- can't click on windows
- can't spin the carousel
2022-07-03 23:36:17 +01:00
Daniel Barlow 9110280373 rename local 2022-07-03 23:17:39 +01:00
Daniel Barlow b34a43897a remove many hard-coded co-ordinates 2022-07-03 23:08:33 +01:00
Daniel Barlow bd18a20251 some generally (I hope) useful config for the Pinephone 2022-07-03 23:08:33 +01:00
Daniel Barlow 27b28e997b nixos module to start kiwmi at boot
this is here for my convenience, I make no claims of practicality
or beauty (it uses nix-shell and hardcodes pathnames)
2022-07-03 23:08:33 +01:00
Daniel Barlow ee2730f757 only resize output when using wayland backend
surprisingly enough, real hardware complains
2022-07-03 23:08:33 +01:00
Daniel Barlow 8683fce4aa use output width/height instead of hardcoding 2022-07-03 23:08:33 +01:00
Daniel Barlow 1875b87592 use output width/height instead of hardcoding 2022-07-03 23:08:33 +01:00
Daniel Barlow 69753c2345 add some matrix functions 2022-07-03 23:08:33 +01:00
Daniel Barlow 07fd321f94 extract texture-from-file into module 2022-07-03 23:08:33 +01:00
Daniel Barlow 67e03c6736 extract function 2022-07-03 11:09:02 +01:00
Daniel Barlow 60d4466857 terminate repl & cleanup when eof 2022-07-02 23:17:00 +01:00
30 changed files with 2150 additions and 138 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*~
result*
*.img

View File

@ -1,9 +1,11 @@
# eufon
**Broken, not ready for use**
> *euphony*: _noun_ Harmonious arrangement of sounds in composition; a smooth and agreeable combination of articulate elements in any piece of writing.
A mostly Fennel-based graphical environment tailored for the Pinephone
(other Linux-based mobile devices exist). The principles we aspire to are
A mostly Fennel-based graphical environment tailored for Linux-based
mobile devices. The principles we aspire to are
* "habitable software" - build the system in such a way that a
technically competent user may change it to serve their needs,
@ -14,24 +16,51 @@ A mostly Fennel-based graphical environment tailored for the Pinephone
hardware buttons, let's play to its strengths instead of compensating for
its weaknesses
As of 2022 these principles are more aspirational than actual.
As of 2022 these principles are more aspirational than actual. _This
repo is basically in an advanced state of brokenness_
## Running it
## Running the shell/apps locally
$ nix-shell --run "kiwmi -c init.lua"
$ nix-shell
nix-shell$ kiwmi -c init.lua
`shell.nix` sets `LUA_PATH` and `LUA_CPATH` settings appropriately -
if you want to write a real derivation (I'll get to it eventually)
you'll need to sort that out yourself. Nix generates a wrapper script
for the Lua interpreter itself, but it doesn't do the same for kiwmi.
This works on desktop or on mobile - Kiwmi is built on wlroots, which
will open a window on your existing Wayland or X11 desktop if you're
running one.
If you're connected over ssh and want to run Kiwmi on the console,
further contortions are required as you don't have the permissions
by default: run this before attempting to start Kiwmi
nix-shell -p seatd --run "sudo -b seatd -u $USER"
## Connecting to the repl
If you are using the example rc.fnl, it opens a Unix socket that you
can connect to and interact with a Fennel REPL. I use
[socat](http://www.dest-unreach.org/socat/) for this purpose:
By default Eufon opens a Unix socket to which you can connect to
interact with a Fennel REPL. The `eufonctl` script is a wrapper around
[socat](http://www.dest-unreach.org/socat/)
$ eufonctl $WAYLAND_DISPLAY
## Building for a device
Eufon can be installed as a [Mobile NixOS](https://github.com/NixOS/mobile-nixos/) module, by adding
`module.nix` to the `imports` in `your configuration.nix`. For example, on my development phone I
have
```nix
imports = [
(import <mobile-nixos/lib/configuration.nix> {
device = "motorola-potter";
})
/home/dan/src/phoen/eufon/module.nix
];
```
Instructions for using Mobile NixOS are currently outside the scope of
this README.
$ socat - unix-connect:${XDG_RUNTIME_DIR}/kiwmi-repl.wayland-1.socket
# TODO
@ -50,7 +79,7 @@ can connect to and interact with a Fennel REPL. I use
- gestures to switch view
- gesture to launch terminal?
- some way to kill an app
- kiwmi may or may not have touch support
- finish adding touch support to Kiwmi - https://github.com/buffet/kiwmi/pull/64
- [ ] some way to add launcher shortcuts for Fennel functions
- [ ] hook up system to session bus, to handle incoming calls

2
bin/eufon.sh Executable file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env bash
kiwmi -V -V -c init.lua

9
bin/eufonctl.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
SOCAT=socat
display=$1
test -n "$display" || display=$WAYLAND_DISPLAY
test -n "$display" || display=wayland-0
socket_name="${XDG_RUNTIME_DIR}/kiwmi-repl.${display}.socket"
${SOCAT} - unix-connect:$socket_name

View File

@ -8,7 +8,7 @@
(local Gtk lgi.Gtk)
(fn relpath [filename]
(.. "crier/" filename))
(.. (os.getenv "EUFON_PATH") "/crier/" filename))
(local dbus-service-attrs
{

View File

@ -10,6 +10,10 @@
, gtk3
, gtk-layer-shell
, lua5_3
, socat
, cpio
, makeWrapper
} :
let
lua = lua5_3;
@ -62,6 +66,29 @@ stdenv.mkDerivation {
gtk3
webkitgtk
];
nativeBuildInputs = [ cpio makeWrapper ];
installPhase = ''
export LUA_PATH="`lua -e 'print(package.path)'`"
export LUA_CPATH="`lua -e 'print(package.cpath)'`"
mkdir -p $out/bin
find . -name kiwmi -prune \
-o -type l -prune \
-o -name git -prune \
-o -print0 | cpio -0 -v --pass-through $out
substitute bin/eufonctl.sh $out/bin/eufonctl \
--replace SOCAT=socat SOCAT=${socat}/bin/socat
makeWrapper ${kiwmi}/bin/kiwmi $out/bin/eufon \
--set LUA_PATH "$out/?.lua;$LUA_PATH" \
--set LUA_CPATH "$LUA_CPATH" \
--prefix PATH : ${kiwmi}/bin:${luaWithPackages}/bin \
--set EUFON_PATH "$out" \
--set GIO_EXTRA_MODULES "${glib-networking}/lib/gio/modules" \
--set GI_TYPELIB_PATH "$GI_TYPELIB_PATH" \
--add-flags "-c $out/init.lua"
chmod +x $out/bin/eufon
'';
src = ./.;
}

200
eufon.fnl Normal file
View File

@ -0,0 +1,200 @@
;; -*- mode: Fennel;
(local { : view } (require :fennel))
(local texture (require :texture))
(local matrix (require :matrix))
(local socket-repl (require :socket-repl))
(local app-state
{
:in-overview false
:focus-view nil
:views []
})
(let [repl-socket-name
(..
(: (os.getenv "XDG_RUNTIME_DIR") :gsub "/$" "")
"/kiwmi-repl."
(os.getenv "WAYLAND_DISPLAY")
".socket"
)]
(socket-repl.start repl-socket-name))
(fn resize-wayland-backend [output]
(when (string.find (output:name) "^WL-")
(output:set_mode 360 720 0)))
(fn kill-window []
(print "DIE")
true)
(fn launch []
(print "WHOOSH")
true)
(fn eufon-path [f]
(.. (os.getenv "EUFON_PATH") "/" f))
(fn show-overview []
(let [facets 64
angle (/ (* 2 math.pi) facets)
scale 0.6
y-offset (+ (* 1 680)
(/ (* 360 facets) (* 2 math.pi)))
]
(each [i view (ipairs app-state.views)]
(doto view
(: :set_matrix
(-> matrix.identity
(matrix.scale scale scale)
(matrix.translate (* 2 180) (+ y-offset))
(matrix.rotate (/ (* (- i 2
) angle) 1))
(matrix.translate (* -2 180) (- y-offset))
(matrix.translate 120 150)
))
(: :show)))))
(fn hide-overview []
(each [k view (pairs app-state.views)]
(view:set_matrix matrix.identity)
(if (= (view:id) (app-state.focus-view:id))
(doto view
(: :show)
(: :focus))
(view:hide))))
(fn carousel []
(let [was app-state.in-overview]
(if was
(hide-overview)
(show-overview))
(: (kiwmi:active_output) :redraw)
(tset app-state :in-overview (not was))))
(fn placements [output]
(let [(width height) (output:size)
bar-height (/ height 15)]
{
:application {
:x 0
:y 0
:w width
:h (- height (* bar-height 1.1))
}
:bar {
:x 0
:y (- height (* bar-height 1.1))
:w width
:h (* bar-height 1.1)
}
:kill {
:x (- (* width 0.25) (/ bar-height 2))
:y (- height bar-height)
:w bar-height
:h bar-height
:on-press kill-window
}
:launch {
:x (- (* width 0.5) (/ bar-height 2))
:y (- height bar-height)
:w bar-height
:h bar-height
:on-press launch
}
:overview {
:x (- (* width 0.75) (/ bar-height 2))
:y (- height bar-height)
:w bar-height
:h bar-height
:on-press carousel
}
}))
(fn choose-view-at [x y]
(let [view (kiwmi:view_at x y)]
(tset app-state :focus-view view)
(tset app-state :in-overview false)
(hide-overview)
true))
(let [cursor (kiwmi:cursor)]
(cursor:on "button_up"
(fn [button-id]
(when app-state.in-overview
(let [geom (placements (kiwmi:active_output))
(x y) (cursor:pos)]
(if (< y geom.application.h)
(choose-view-at x y)
false))))))
(kiwmi:on
"output"
(fn [output]
(resize-wayland-backend output)
(let [(width height) (output:size)
r (output:renderer)
kill (texture.from-file r (eufon-path "close-window.png"))
launch (texture.from-file r (eufon-path "launcher.png"))
overview (texture.from-file r (eufon-path "carousel.png"))]
(output:on "render"
(fn [{: output : renderer}]
(let [buttons (placements output)]
(renderer:draw_rect :#77000077
buttons.bar.x buttons.bar.y
buttons.bar.w buttons.bar.h)
(renderer:draw_texture
kill
matrix.identity
buttons.kill.x buttons.kill.y
0.7)
(renderer:draw_texture
launch
matrix.identity
buttons.launch.x buttons.launch.y
0.7)
(renderer:draw_texture
overview
matrix.identity
buttons.overview.x buttons.overview.y
0.7)))))))
(let [cursor (kiwmi:cursor)]
(cursor:on "button_down"
(fn [button]
(let [(x y) (cursor:pos)]
(each [name attribs (pairs (placements (kiwmi:active_output)))]
(if (and
(<= attribs.x x)
(< x (+ attribs.x attribs.w))
(<= attribs.y y)
(< y (+ attribs.y attribs.h)))
(if attribs.on-press (attribs.on-press))))))))
(kiwmi:on "view"
(fn [view]
(let [geom (placements (kiwmi:active_output))]
(view:resize geom.application.w geom.application.h)
(view:move geom.application.x geom.application.y))
(view:focus)
(view:show)
(tset app-state :focus-view view)
(table.insert app-state.views view)
(view:on "request_move" #(view:imove))
(view:on "request_resize" (fn [ev] (view:iresize ev.edges)))))
(kiwmi:spawn "swaybg -c '#ff00ff'")
(kiwmi:spawn "lua -l fennelrun modeline")
;(kiwmi:spawn "lua -l fennelrun saturn")
(kiwmi:spawn "lua -l fennelrun crier")
(kiwmi:spawn "lua -l fennelrun just https://brvt.telent.net")
;(kiwmi:spawn "foot")
;; Local Variables:
;; inferior-lisp-program: "eufonctl"
;; End:

View File

@ -1,4 +1,6 @@
local fennel = require("fennel")
table.insert(package.loaders or package.searchers, fennel.searcher)
-- print(fennel.view(arg))
fennel.dofile(arg[0], { correlate = true })
fennel.path = os.getenv("EUFON_PATH") .. "/?.fnl;" ..
os.getenv("EUFON_PATH") .. "/?/init.fnl;"
require(arg[0])

View File

@ -3,4 +3,5 @@ print(os.getenv("LUA_CPATH"))
local fennel = require("fennel")
table.insert(package.loaders or package.searchers, fennel.searcher)
fennel.dofile("rc.fnl")
fennel.path = fennel.path .. ";" .. os.getenv("EUFON_PATH") .. "/?.fnl"
require("eufon")

View File

@ -39,6 +39,10 @@ struct kiwmi_cursor {
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
struct wl_listener cursor_frame;
struct wl_listener cursor_touch_up;
struct wl_listener cursor_touch_down;
struct wl_listener cursor_touch_motion;
struct wl_listener cursor_touch_frame;
struct {
struct wl_signal button_down;
@ -46,6 +50,7 @@ struct kiwmi_cursor {
struct wl_signal destroy;
struct wl_signal motion;
struct wl_signal scroll;
struct wl_signal touch;
} events;
};
@ -61,6 +66,14 @@ struct kiwmi_cursor_motion_event {
double newy;
};
struct kiwmi_cursor_touch_event {
char *event;
int id;
double x;
double y;
bool handled;
};
struct kiwmi_cursor_scroll_event {
const char *device_name;
bool is_vertical;

View File

@ -15,6 +15,7 @@ struct kiwmi_input {
struct wl_listener new_input;
struct kiwmi_cursor *cursor;
struct kiwmi_seat *seat;
int touchpads;
struct {
struct wl_signal keyboard_new;

1342
kiwmi/include/vvector.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,8 @@
#include "input/seat.h"
#include "server.h"
#include "vvector.h"
void
view_close(struct kiwmi_view *view)
{
@ -182,13 +184,30 @@ view_at(
double *sx,
double *sy)
{
float xy[2] = { lx, ly };
float xy_[2];
float transform[3][3];
float inv_transform[3][3];
float det;
struct kiwmi_view *view;
wl_list_for_each (view, &desktop->views, link) {
if (view->hidden || !view->mapped) {
continue;
}
if (surface_at(view, surface, lx, ly, sx, sy)) {
/*
The view may have transformation matrix, so we need to map
lx, ly into the view co-ordinate space before calling
surface_at. Or possibly vice versa. This stuff makes my head
hurt.
*/
memcpy(transform, view->matrix, sizeof transform);
INVERT_3X3(inv_transform, det, transform);
MAT_DOT_VEC_2X3(xy_, inv_transform, xy);
if (surface_at(view, surface, xy_[0], xy_[1], sx, sy)) {
return view;
}
}

View File

@ -14,6 +14,7 @@
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
@ -119,6 +120,130 @@ cursor_motion_notify(struct wl_listener *listener, void *data)
process_cursor_motion(server, event->time_msec);
}
static void
cursor_touch_down_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_cursor *cursor =
wl_container_of(listener, cursor, cursor_touch_down);
struct kiwmi_server *server = cursor->server;
struct kiwmi_desktop *desktop = &server->desktop;
struct wlr_event_touch_down *event = data;
struct kiwmi_input *input = &server->input;
struct wlr_seat *seat = input->seat->seat;
struct wlr_surface *surface = NULL;
/* FIXME figure out what scaling should be done here.
* Does the touch co-ordinate system map onto a single output
* (e.g. phone screen) or onto the entire layout (tablet,
* maybe?)
*/
double lx = 720 * event->x;
double ly = 1440 * event->y;
double sx, sy;
struct kiwmi_view *view = view_at(
desktop,
lx, ly,
&surface,
&sx, &sy);
wlr_log(WLR_DEBUG, "view %x surface %x %f,%f %f,%f\n",
view, surface, lx, ly, sx, sy);
/* we send the event to lua with 0..1 co-ordinates, because
* it may not be over any surface
*/
struct kiwmi_cursor_touch_event new_event = {
.event = "down",
.id = event->touch_id,
.x = event->x,
.y = event->y,
};
wl_signal_emit(&cursor->events.touch, &new_event);
if(!new_event.handled &&
surface &&
wlr_surface_accepts_touch(seat, surface)) {
wlr_log(WLR_DEBUG, "surface %x %f %f\n",
surface, sx, sy);
wlr_seat_touch_notify_down(seat, surface, event->time_msec,
event->touch_id, sx, sy);
}
}
static void
cursor_touch_up_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_cursor *cursor =
wl_container_of(listener, cursor, cursor_touch_up);
struct kiwmi_server *server = cursor->server;
struct wlr_event_touch_up *event = data;
struct kiwmi_input *input = &server->input;
struct wlr_seat *seat = input->seat->seat;
struct kiwmi_cursor_touch_event new_event = {
.event = "up",
.id = event->touch_id,
};
wl_signal_emit(&cursor->events.touch, &new_event);
if(!new_event.handled) {
wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
}
}
static void
cursor_touch_motion_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_cursor *cursor =
wl_container_of(listener, cursor, cursor_touch_motion);
struct kiwmi_server *server = cursor->server;
struct wlr_event_touch_motion *event = data;
struct kiwmi_input *input = &server->input;
struct wlr_seat *seat = input->seat->seat;
struct kiwmi_cursor_touch_event new_event = {
.event = "motion",
.id = event->touch_id,
.x = event->x,
.y = event->y,
};
wl_signal_emit(&cursor->events.touch, &new_event);
if(!new_event.handled) {
/* UNSURE: should we still send this even if the touch_down
* didn't get sent because the surface doesn't accept
* touch? */
wlr_seat_touch_notify_motion(seat, event->time_msec,
event->touch_id, event->x, event->y);
}
}
static void
cursor_touch_frame_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_cursor *cursor =
wl_container_of(listener, cursor, cursor_touch_frame);
struct kiwmi_server *server = cursor->server;
struct kiwmi_input *input = &server->input;
struct wlr_seat *seat = input->seat->seat;
struct kiwmi_cursor_touch_event new_event = {
.event = "frame",
};
wl_signal_emit(&cursor->events.touch, &new_event);
if(!new_event.handled) {
wlr_seat_touch_notify_frame(seat);
}
}
static void
cursor_motion_absolute_notify(struct wl_listener *listener, void *data)
{
@ -252,10 +377,23 @@ cursor_create(
cursor->cursor_frame.notify = cursor_frame_notify;
wl_signal_add(&cursor->cursor->events.frame, &cursor->cursor_frame);
cursor->cursor_touch_down.notify = cursor_touch_down_notify;
wl_signal_add(&cursor->cursor->events.touch_down, &cursor->cursor_touch_down);
cursor->cursor_touch_up.notify = cursor_touch_up_notify;
wl_signal_add(&cursor->cursor->events.touch_up, &cursor->cursor_touch_up);
cursor->cursor_touch_motion.notify = cursor_touch_motion_notify;
wl_signal_add(&cursor->cursor->events.touch_motion, &cursor->cursor_touch_motion);
cursor->cursor_touch_frame.notify = cursor_touch_frame_notify;
wl_signal_add(&cursor->cursor->events.touch_frame, &cursor->cursor_touch_frame);
wl_signal_init(&cursor->events.button_down);
wl_signal_init(&cursor->events.button_up);
wl_signal_init(&cursor->events.destroy);
wl_signal_init(&cursor->events.motion);
wl_signal_init(&cursor->events.touch);
wl_signal_init(&cursor->events.scroll);
return cursor;

View File

@ -28,6 +28,13 @@ new_pointer(struct kiwmi_input *input, struct wlr_input_device *device)
wlr_cursor_attach_input_device(input->cursor->cursor, device);
}
static void
new_touch(struct kiwmi_input *input, struct wlr_input_device *device)
{
wlr_cursor_attach_input_device(input->cursor->cursor, device);
input->touchpads++;
}
static void
new_keyboard(struct kiwmi_input *input, struct wlr_input_device *device)
{
@ -53,6 +60,9 @@ new_input_notify(struct wl_listener *listener, void *data)
case WLR_INPUT_DEVICE_POINTER:
new_pointer(input, device);
break;
case WLR_INPUT_DEVICE_TOUCH:
new_touch(input, device);
break;
case WLR_INPUT_DEVICE_KEYBOARD:
new_keyboard(input, device);
break;
@ -65,6 +75,9 @@ new_input_notify(struct wl_listener *listener, void *data)
if (!wl_list_empty(&input->keyboards)) {
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
}
if(input->touchpads) {
caps |= WL_SEAT_CAPABILITY_TOUCH;
}
wlr_seat_set_capabilities(input->seat->seat, caps);
}

View File

@ -162,6 +162,36 @@ kiwmi_cursor_on_motion_notify(struct wl_listener *listener, void *data)
}
}
static void
kiwmi_cursor_on_touch_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener);
struct kiwmi_server *server = lc->server;
lua_State *L = server->lua->L;
struct kiwmi_cursor_touch_event *event = data;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_newtable(L);
lua_pushnumber(L, event->x);
lua_setfield(L, -2, "x");
lua_pushnumber(L, event->y);
lua_setfield(L, -2, "y");
lua_pushnumber(L, event->id);
lua_setfield(L, -2, "id");
lua_pushstring(L, event->event);
lua_setfield(L, -2, "name");
if (lua_pcall(L, 1, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
static void
kiwmi_cursor_on_scroll_notify(struct wl_listener *listener, void *data)
{
@ -267,6 +297,31 @@ l_kiwmi_cursor_on_motion(lua_State *L)
return 0;
}
static int
l_kiwmi_cursor_on_touch(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_cursor");
luaL_checktype(L, 2, LUA_TFUNCTION);
struct kiwmi_cursor *cursor = obj->object;
struct kiwmi_server *server = cursor->server;
lua_pushcfunction(L, luaK_kiwmi_lua_callback_new);
lua_pushlightuserdata(L, server);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, kiwmi_cursor_on_touch_notify);
lua_pushlightuserdata(L, &cursor->events.touch);
lua_pushlightuserdata(L, obj);
if (lua_pcall(L, 5, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 0;
}
static int
l_kiwmi_cursor_on_scroll(lua_State *L)
{
@ -296,6 +351,7 @@ static const luaL_Reg kiwmi_cursor_events[] = {
{"button_down", l_kiwmi_cursor_on_button_down},
{"button_up", l_kiwmi_cursor_on_button_up},
{"motion", l_kiwmi_cursor_on_motion},
{"touch", l_kiwmi_cursor_on_touch},
{"scroll", l_kiwmi_cursor_on_scroll},
{NULL, NULL},
};

View File

@ -216,20 +216,21 @@ kiwmi_server_event_loop_fd_handler(int fd, uint32_t mask, void *data)
lua_State *L = lc->server->lua->L;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_pushvalue(L, -1);
lua_pushinteger(L, fd);
lua_pushinteger(L, mask);
if (lua_pcall(L, 2, 0, 0)) {
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
}
/*
FIXME we need to call
/* if callback returns false/nil, remove the handler */
if (! lua_toboolean(L, -1)) {
wl_event_source_remove(lc->event_source);
luaL_unref(L, LUA_REGISTRYINDEX, lc->callback_ref);
free(lc);
when the file is closed, but probably not before then
*/
}
lua_pop(L, -1);
return true;
}
@ -242,7 +243,6 @@ l_kiwmi_server_event_loop_add_fd(lua_State *L)
struct kiwmi_server *server = obj->object;
int fd = lua_tonumber(L, 2);
int mode = lua_tonumber(L, 3);
luaL_checktype(L, 4, LUA_TFUNCTION); // callback
@ -265,12 +265,14 @@ l_kiwmi_server_event_loop_add_fd(lua_State *L)
/* luaL_ref pops last parameter (callback) from stack */
lc->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
wl_event_loop_add_fd(server->wl_event_loop,
fd,
event_types,
kiwmi_server_event_loop_fd_handler,
lc);
return true;
lc->event_source =
wl_event_loop_add_fd(server->wl_event_loop,
fd,
event_types,
kiwmi_server_event_loop_fd_handler,
lc);
return 0;
}

View File

@ -40,6 +40,9 @@ whenever there is activity on the file descriptor `fd`. The `mode` parameter
is one of `O_RDWR`, `O_RDONLY`, `O_WRONLY` as defined in luaposix
`posix.fcntl` module.
If `callback` returns false or nil, the handler will be unregistered
from the event loop and not called again.
#### kiwmi:focused_view()
Returns the currently focused view.

55
matrix.fnl Normal file
View File

@ -0,0 +1,55 @@
;; homogeneous matrix operations for 2d graphics
(local identity
[
1 0 0
0 1 0
0 0 1
])
(fn multiply-matrix [a b]
[
(+ (* (. a 1) (. b 1)) (* (. a 2) (. b 4)) (* (. a 3) (. b 7)))
(+ (* (. a 1) (. b 2)) (* (. a 2) (. b 5)) (* (. a 3) (. b 8)))
(+ (* (. a 1) (. b 3)) (* (. a 2) (. b 6)) (* (. a 3) (. b 9)))
(+ (* (. a 4) (. b 1)) (* (. a 5) (. b 4)) (* (. a 6) (. b 7)))
(+ (* (. a 4) (. b 2)) (* (. a 5) (. b 5)) (* (. a 6) (. b 8)))
(+ (* (. a 4) (. b 3)) (* (. a 5) (. b 6)) (* (. a 6) (. b 9)))
(+ (* (. a 7) (. b 1)) (* (. a 8) (. b 4)) (* (. a 9) (. b 7)))
(+ (* (. a 7) (. b 2)) (* (. a 8) (. b 5)) (* (. a 9) (. b 8)))
(+ (* (. a 7) (. b 3)) (* (. a 8) (. b 6)) (* (. a 9) (. b 9)))
])
(fn scale [matrix x y]
(let [scale [
x 0 0
0 y 0
0 0 1]]
(multiply-matrix matrix scale)))
(fn translate [matrix x y]
(let [translation [
1 0 x
0 1 y
0 0 1]]
(multiply-matrix matrix translation)))
(fn rotate [matrix rad]
(let [sin (math.sin rad)
cos (math.cos rad)
rotation [
cos (- sin) 0
sin cos 0
0 0 1
]]
(multiply-matrix matrix rotation)))
{
: identity
: scale
: translate
: rotate
}

View File

@ -9,7 +9,7 @@
;(local battery (require :blinkenlicht.metric.battery))
(local cpustat (require :blinkenlicht.metric.cpustat))
(stylesheet "modeline.css")
(stylesheet (.. (os.getenv "EUFON_PATH") "/modeline.css"))
(fn battery-icon-codepoint [status percent]
(if (= status "Charging") 0xf0e7

67
module.nix Normal file
View File

@ -0,0 +1,67 @@
{ config, lib, pkgs, ... }:
let eufon = pkgs.callPackage ./default.nix {}; in
{
systemd.services."eufon" = {
wants = [
"systemd-machined.service"
"accounts-daemon.service"
"systemd-udev-settle.service"
"dbus.socket"
];
aliases = [ "display-manager.service" ];
after = [
"rc-local.service"
"systemd-machined.service"
"systemd-user-sessions.service"
"getty@tty2.service"
"plymouth-quit.service"
"plymouth-start.service"
"systemd-logind.service"
"systemd-udev-settle.service"
];
conflicts = [
"getty@tty1.service" # not sure if needed
"getty@tty2.service"
"plymouth-quit.service"
];
wantedBy = [ "multi-user.target" ];
serviceConfig =
let run-eufon = pkgs.writeScript "run-eufon" ''
#!${pkgs.bash}/bin/bash
source ${config.system.build.setEnvironment}
${pkgs.dbus}/bin/dbus-run-session ${eufon}/bin/eufon
systemd-cat echo "dbus-run-session $?"
'';
in {
WorkingDirectory = "${eufon}";
TTYPath = "/dev/tty2";
TTYReset = "yes";
TTYVHangup = "yes";
TTYVTDisallocate = "yes";
PAMName = "login";
StandardInput = "tty";
StandardError = "journal";
StandardOutput = "journal";
SyslogIdentifier = "eufon";
User = "dan";
ExecStart = run-eufon;
Restart = "always";
};
environment = {
NIX_PATH = "nixpkgs=${<nixpkgs>}";
};
};
environment.systemPackages = with pkgs; [
git eufon
];
boot.postBootCommands = lib.mkOrder (-1) ''
brightness=4000
echo "Setting brightness to $brightness"
echo $brightness > /sys/class/backlight/wled/brightness
'';
services.logind.extraConfig = ''
HandlePowerKey=ignore
'';
}

59
pinephone.nix Normal file
View File

@ -0,0 +1,59 @@
{ config, lib, pkgs, ... }:
# configuration.nix contains settings applicable to _my_ pinephone
# pinephone.nix contains settings applicable to eufon on pinephones.
# module.nix contains settings applicable to eufon generally
{
config = {
powerManagement = {
enable = true;
cpuFreqGovernor = "ondemand";
};
mobile.boot.stage-1.firmware = [
config.mobile.device.firmware
];
hardware.sensor.iio.enable = true;
hardware.firmware = [ config.mobile.device.firmware ];
services.fwupd = {
enable = true;
};
environment.systemPackages =
let refresh-bootfs = (import ./refresh-bootfs.nix { inherit config pkgs lib; });
in with pkgs; [
dtc
file
refresh-bootfs
];
environment.etc."fwupd/remotes.d/testing.conf" = {
mode = "0644";
text = ''
[fwupd Remote]
Enabled=true
Title=Linux Vendor Firmware Service (testing)
MetadataURI=https://cdn.fwupd.org/downloads/firmware-testing.xml.gz
ReportURI=https://fwupd.org/lvfs/firmware/report
OrderBefore=lvfs,fwupd
AutomaticReports=false
ApprovalRequired=false
'';
};
nixpkgs = {
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
"pine64-pinephone-firmware"
];
};
# boot.loader.generic-extlinux-compatible.enable = lib.mkForce true;
hardware.opengl = {
enable = true;
driSupport = true;
};
};
}

101
rc.fnl
View File

@ -1,101 +0,0 @@
(local { : GdkPixbuf } (require :lgi))
(local { : view } (require :fennel))
(local socket-repl (require :socket-repl))
(let [repl-socket-name
(..
(: (os.getenv "XDG_RUNTIME_DIR") :gsub "/$" "")
"/kiwmi-repl."
(os.getenv "WAYLAND_DISPLAY")
".socket"
)]
(socket-repl.start repl-socket-name))
(fn texture-from-file [renderer filename]
(let [pixels
(let [(buf err) (GdkPixbuf.Pixbuf.new_from_file filename)]
(if (not buf) (print :err err))
buf)]
(renderer:texture_from_pixels
pixels.rowstride
pixels.width
pixels.height
(pixels:get_pixels))))
(kiwmi:on
"output"
(fn [output]
(output:set_mode 360 720 0)
(let [r (output:renderer)
kill (texture-from-file r "close-window.png")
launch (texture-from-file r "launcher.png")
spinner (texture-from-file r "carousel.png")]
(output:on "render"
(fn [{: output : renderer}]
(let [bar-height 40
matrix [1 0 0
0 1 0
0 0 1]]
(renderer:draw_rect :#00000077
0 (- 720 bar-height)
690 360 bar-height)
(renderer:draw_texture
kill
matrix
30 (- 720 bar-height)
0.7)
(renderer:draw_texture
launch matrix
(- 180 (/ bar-height 2)) (- 720 bar-height)
0.7)
(renderer:draw_texture
spinner
matrix
(- 360 30 bar-height) (- 720 bar-height)
0.7)))))))
(fn kill-window []
(print "DIE")
true)
(fn launch []
(print "WHOOSH")
true)
(fn carousel []
(print "spin spin sugar")
true)
(let [cursor (kiwmi:cursor)]
(cursor:on "button_down"
(fn [button]
(let [(x y) (cursor:pos)]
(if (> y 680)
(if (< x 70)
(kill-window)
(and (< 150 x) (< x 190))
(launch)
(and (< 285 x) (< x 330))
(carousel)
false))))))
(kiwmi:on "view"
(fn [view]
(let [(w h) (: (kiwmi:active_output) :size)]
(view:resize w h)
(view:move 0 0))
(view:focus)
(view:show)
(view:on "request_move" #(view:imove))
(view:on "request_resize" (fn [ev] (view:iresize ev.edges)))))
;(kiwmi:spawn "swaybg -c '#ff00ff'")
(kiwmi:spawn "lua -l fennelrun modeline.fnl")
(kiwmi:spawn "lua -l fennelrun saturn/main.fnl")
(kiwmi:spawn "lua -l fennelrun crier/crier.fnl")
(kiwmi:spawn "lua -l fennelrun just/just.fnl")
;(kiwmi:spawn "foot")

43
refresh-bootfs.nix Normal file
View File

@ -0,0 +1,43 @@
{ config, pkgs, lib, ... }:
# nixos/mobile-nixos don't currently (June 2022) update the kernel and
# initrd used by the bootloader when nixos-rebuild is run. This is a
# workaround until they do. Mount your boot filesystem somewhere
# and run "refresh-bootfs /path/to/mounted/bootfs" after switching
# configuration
let
inherit (config.mobile.outputs) recovery stage-0;
inherit (pkgs) writeScriptBin buildPackages imageBuilder runCommandNoCC;
kernel = stage-0.mobile.boot.stage-1.kernel.package;
kernel_file = "${kernel}/${if kernel ? file then kernel.file else pkgs.stdenv.hostPlatform.linux-kernel.target}";
# bootscr = runCommandNoCC "boot.scr" {
# nativeBuildInputs = [
# buildPackages.ubootTools
# ];
# } ''
# mkimage -C none -A arm64 -T script -d {bootcmd} $out
# '';
in writeScriptBin "refresh-bootfs" ''
#!${pkgs.runtimeShell}
test -n "$1" || exit 1
test -d "$1" || exit 1
cd $1
test -f ./boot.scr || exit 1
mkdir -vp mobile-nixos/{boot,recovery}
(
cd mobile-nixos/boot
cp -v ${stage-0.mobile.outputs.initrd} stage-1
cp -v ${kernel_file} kernel
cp -vr ${kernel}/dtbs dtbs
)
(
cd mobile-nixos/recovery
cp -v ${recovery.mobile.outputs.initrd} stage-1
cp -v ${kernel_file} kernel
cp -vr ${kernel}/dtbs dtbs
)
# cp -v {bootscr} ./boot.scr
''

2
run.sh Executable file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env bash
nix-shell --run "start_eufon"

View File

@ -5,5 +5,6 @@ in (p.overrideAttrs (o:{
shellHook = ''
export LUA_PATH=`lua -e 'print(package.path)'`
export LUA_CPATH=`lua -e 'print(package.cpath)'`
export EUFON_PATH=`pwd`
'';
})).override { debug = true; }

View File

@ -20,17 +20,29 @@
(on-connected connected-fd)))
))
(fn get-input [fd]
(let [buf (unistd.read fd 1024)]
(if (and buf (> (# buf) 0))
buf
"\n,exit")))
(fn start [pathname]
(print pathname)
(unistd.unlink pathname)
(unix-socket-listener
pathname
(fn [fd]
(let [sock (fdopen fd "w+")
repl-coro (coroutine.create repl)]
(print :fd fd :sock sock)
(watch-fd fd fcntl.O_RDONLY
#(coroutine.resume repl-coro (unistd.read $1 1024)))
(fn [fd]
(coroutine.resume repl-coro (get-input fd))
(if (= (coroutine.status repl-coro) :dead)
(do
(sock:write "bye!\n")
(sock:close)
false)
true
)))
(coroutine.resume repl-coro
{:readChunk
(fn [{: stack-size}]

14
texture.fnl Normal file
View File

@ -0,0 +1,14 @@
(local { : GdkPixbuf } (require :lgi))
(fn from-file [renderer filename]
(let [pixels
(let [(buf err) (GdkPixbuf.Pixbuf.new_from_file filename)]
(if (not buf) (print :err err))
buf)]
(renderer:texture_from_pixels
pixels.rowstride
pixels.width
pixels.height
(pixels:get_pixels))))
{ : from-file }