mirror of
https://github.com/NixOS/mobile-nixos.git
synced 2024-12-04 21:18:28 +03:00
boot/lib: Import lvgui ruby implementation
This commit is contained in:
parent
b88d2565ee
commit
ffc4a12fa1
50
boot/lib/lvgui/args.rb
Normal file
50
boot/lib/lvgui/args.rb
Normal file
@ -0,0 +1,50 @@
|
||||
# Extremely minimal and naïve arguments parsing.
|
||||
module Args
|
||||
@@parsed = nil
|
||||
|
||||
def self.get(key, default = nil)
|
||||
parse[:values][key] or default
|
||||
end
|
||||
|
||||
def self.unused()
|
||||
parsed[:unused]
|
||||
end
|
||||
|
||||
def self.parse()
|
||||
return @@parsed if @@parsed
|
||||
|
||||
argv = ARGV.dup
|
||||
unused = []
|
||||
|
||||
# Default values.
|
||||
# `nil` means it expects a parameter.
|
||||
# `false` means it's a boolean toggle without parameter.
|
||||
values = {}
|
||||
while argv.length > 0
|
||||
arg = argv.shift
|
||||
if arg.match(/^--help$/)
|
||||
# Assumes the programmer will add it
|
||||
begin
|
||||
print_help
|
||||
rescue
|
||||
$stderr.puts "Sorry, no help defined."
|
||||
end
|
||||
exit 0
|
||||
elsif arg.match(/^--/)
|
||||
key = arg.sub(/^--/, "").gsub("-", "_").to_sym
|
||||
if argv.length > 0 && !argv[0].match(/^--/)
|
||||
values[key] = argv.shift
|
||||
else
|
||||
values[key] = true
|
||||
end
|
||||
else
|
||||
unused << arg
|
||||
end
|
||||
end
|
||||
|
||||
@@parsed = {
|
||||
unused: unused,
|
||||
values: values,
|
||||
}
|
||||
end
|
||||
end
|
100
boot/lib/lvgui/fiddlier.rb
Normal file
100
boot/lib/lvgui/fiddlier.rb
Normal file
@ -0,0 +1,100 @@
|
||||
# This module augments Fiddle with useful helpers.
|
||||
|
||||
module LVGL
|
||||
end
|
||||
module LVGL::Fiddlier
|
||||
# Given a +type+, returns the value of the global +name+.
|
||||
def get_global!(type, name)
|
||||
addr = handler.sym(name)
|
||||
raise(Fiddle::DLError, "cannot find symbol #{name}") unless addr
|
||||
s = struct(["#{type} value"])
|
||||
s.new(addr).value
|
||||
end
|
||||
|
||||
# Given a +type+, sets the global +name+ to +new_value+.
|
||||
def set_global!(type, name, new_value)
|
||||
addr = handler.sym(name)
|
||||
raise(Fiddle::DLError, "cannot find symbol #{name}") unless addr
|
||||
s = struct(["#{type} value"])
|
||||
tmp = s.new(addr)
|
||||
tmp.value = new_value
|
||||
tmp.value
|
||||
end
|
||||
|
||||
def get_global_struct!(struct, name)
|
||||
addr = handler.sym(name)
|
||||
raise(Fiddle::DLError, "cannot find symbol for struct #{name}") unless addr
|
||||
struct.new(addr)
|
||||
end
|
||||
|
||||
# Using +set_global!+ and +get_global!+, creates accessors for a global
|
||||
# variable.
|
||||
def global!(type, name)
|
||||
method_name = name.to_sym
|
||||
define_method(method_name) do
|
||||
get_global!(type, name)
|
||||
end
|
||||
module_function method_name
|
||||
|
||||
method_name = "#{name}=".to_sym
|
||||
define_method(method_name) do |value|
|
||||
set_global!(type, name, value)
|
||||
end
|
||||
module_function method_name
|
||||
end
|
||||
|
||||
# TODO: parse `typedef enum {...} name;`
|
||||
def enum!(name, values, type: "int")
|
||||
typedef name.to_s, type.to_s
|
||||
mod = self.const_set(name.to_sym, Module.new)
|
||||
current_value = 0
|
||||
values.each do |data|
|
||||
if data.is_a? Hash
|
||||
name = data.keys.first.to_sym
|
||||
current_value = data.values.first
|
||||
else
|
||||
name = data.to_sym
|
||||
end
|
||||
mod.const_set(name, current_value)
|
||||
|
||||
current_value += 1
|
||||
end
|
||||
end
|
||||
|
||||
# Flattens the given nested struct
|
||||
def flatten_struct!(fields, prefix: nil)
|
||||
fields.map do |field|
|
||||
type = field.first
|
||||
name = [prefix, field.last].compact.join("_")
|
||||
|
||||
if type.is_a? Array then
|
||||
flatten_struct!(type, prefix: name)
|
||||
else
|
||||
[[type.to_sym, name.to_sym]]
|
||||
end
|
||||
end.flatten(1)
|
||||
end
|
||||
|
||||
# Parses nested structs into a flattened Fiddle struct.
|
||||
# XXX: broken because of struct alignment padding / packing
|
||||
# -> http://www.catb.org/esr/structure-packing/#_padding
|
||||
def struct!(fields)
|
||||
flattened = flatten_struct!(fields).map do |field|
|
||||
type = field.first
|
||||
name = field.last
|
||||
"#{type} #{name}"
|
||||
end
|
||||
struct(flattened)
|
||||
end
|
||||
|
||||
# Define the given +sym+ as a function.
|
||||
# It will auto-wrap the method using a closure.
|
||||
def bound_method!(sym, sig)
|
||||
sym = sym.to_sym
|
||||
module_function sym
|
||||
ctx = self
|
||||
@func_map[sym.to_s] = bind(sig) do |*args|
|
||||
ctx.send(sym, *args)
|
||||
end
|
||||
end
|
||||
end
|
2
boot/lib/lvgui/lvgl/__init.rb
Normal file
2
boot/lib/lvgui/lvgl/__init.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module LVGL
|
||||
end
|
437
boot/lib/lvgui/lvgl/ffi.rb
Normal file
437
boot/lib/lvgui/lvgl/ffi.rb
Normal file
@ -0,0 +1,437 @@
|
||||
# FFI bindings to LVGL.
|
||||
|
||||
module LVGL::FFI
|
||||
extend Fiddle::BasicTypes
|
||||
extend Fiddle::Importer
|
||||
extend LVGL::Fiddlier
|
||||
|
||||
dlload("liblvgui.so")
|
||||
|
||||
# Alias all built-in types to their [u]intXX_t variants.
|
||||
[
|
||||
:SHORT,
|
||||
:LONG,
|
||||
:LONG_LONG,
|
||||
:CHAR,
|
||||
:INT,
|
||||
].each do |type|
|
||||
[
|
||||
"",
|
||||
"unsigned",
|
||||
].each do |signedness|
|
||||
sz = Fiddle.const_get("SIZEOF_#{type}".to_sym) * 8
|
||||
alias_name = "int#{sz}_t"
|
||||
aliased_type = type.to_s.downcase.gsub("_", " ")
|
||||
|
||||
if signedness == "unsigned"
|
||||
alias_name = "u#{alias_name}"
|
||||
aliased_type = "#{signedness} #{aliased_type}"
|
||||
end
|
||||
typealias(alias_name, aliased_type)
|
||||
end
|
||||
end
|
||||
typealias("bool", "uint8_t")
|
||||
|
||||
# lv_conf.h
|
||||
typealias("lv_coord_t", "int16_t")
|
||||
typedef "lv_obj_user_data_t", "void *"
|
||||
|
||||
# lvgl/src/lv_misc/lv_color.h
|
||||
typealias("lv_color_t", "uint32_t")
|
||||
typealias("lv_opa_t", "uint8_t")
|
||||
|
||||
# introspection.h
|
||||
extern "bool lv_introspection_is_simulator()"
|
||||
extern "bool lv_introspection_is_debug()"
|
||||
extern "bool lv_introspection_use_assert_style()"
|
||||
|
||||
# lvgl/src/lv_misc/lv_task.h
|
||||
enum!(:LV_TASK_PRIO, [
|
||||
:OFF,
|
||||
:LOWEST,
|
||||
:LOW,
|
||||
:MID,
|
||||
:HIGH,
|
||||
:HIGHEST,
|
||||
], type: "uint8_t")
|
||||
typealias("lv_task_prio_t", "LV_TASK_PRIO")
|
||||
|
||||
# lvgl/src/lv_themes/lv_theme.h
|
||||
extern "void lv_theme_set_current(lv_theme_t *)"
|
||||
extern "lv_theme_t * lv_theme_get_current(void)"
|
||||
|
||||
# lvgl/src/lv_themes/lv_theme_night.h
|
||||
extern "lv_theme_t * lv_theme_night_init(uint16_t, lv_font_t *)"
|
||||
extern "lv_theme_t * lv_theme_get_night(void)"
|
||||
|
||||
# lvgl/src/lv_core/lv_obj.h
|
||||
enum!(:LV_EVENT, [
|
||||
:PRESSED, # < The object has been pressed*/
|
||||
:PRESSING, # < The object is being pressed (called continuously while pressing)*/
|
||||
:PRESS_LOST, # < User is still pressing but slid cursor/finger off of the object */
|
||||
:SHORT_CLICKED, # < User pressed object for a short period of time, then released it. Not called if dragged. */
|
||||
:LONG_PRESSED, # < Object has been pressed for at least `LV_INDEV_LONG_PRESS_TIME`. Not called if dragged.*/
|
||||
:LONG_PRESSED_REPEAT, # < Called after `LV_INDEV_LONG_PRESS_TIME` in every
|
||||
# `LV_INDEV_LONG_PRESS_REP_TIME` ms. Not called if dragged.*/
|
||||
:CLICKED, # < Called on release if not dragged (regardless to long press)*/
|
||||
:RELEASED, # < Called in every cases when the object has been released*/
|
||||
:DRAG_BEGIN,
|
||||
:DRAG_END,
|
||||
:DRAG_THROW_BEGIN,
|
||||
:KEY,
|
||||
:FOCUSED,
|
||||
:DEFOCUSED,
|
||||
:VALUE_CHANGED, # < The object's value has changed (i.e. slider moved) */
|
||||
:INSERT,
|
||||
:REFRESH,
|
||||
:APPLY, # < "Ok", "Apply" or similar specific button has clicked*/
|
||||
:CANCEL, # < "Close", "Cancel" or similar specific button has clicked*/
|
||||
:DELETE, # < Object is being deleted */
|
||||
|
||||
], type: "uint8_t")
|
||||
typealias("lv_event_t", "LV_EVENT")
|
||||
typedef "lv_event_cb_t", "void (*lv_event_cb_t)(struct _lv_obj_t *, lv_event_t)"
|
||||
#typedef uint8_t lv_res_t;
|
||||
enum!(:LV_RES, [
|
||||
{ :INV => 0x00 },
|
||||
{ :OK => 0x01 },
|
||||
], type: :uint8_t)
|
||||
typealias("lv_res_t", "LV_RES")
|
||||
|
||||
extern "lv_obj_t * lv_obj_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "const lv_style_t * lv_obj_get_style(const lv_obj_t *)"
|
||||
extern "void lv_obj_set_style(lv_obj_t *, const lv_style_t *)"
|
||||
extern "lv_coord_t lv_obj_get_width(const lv_obj_t *)"
|
||||
extern "lv_coord_t lv_obj_get_height(const lv_obj_t *)"
|
||||
extern "lv_coord_t lv_obj_get_width_fit(const lv_obj_t *)"
|
||||
extern "lv_coord_t lv_obj_get_height_fit(const lv_obj_t *)"
|
||||
extern "void lv_obj_set_width(lv_obj_t *, lv_coord_t)"
|
||||
extern "void lv_obj_set_height(lv_obj_t *, lv_coord_t)"
|
||||
extern "lv_coord_t lv_obj_get_x(const lv_obj_t *)"
|
||||
extern "lv_coord_t lv_obj_get_y(const lv_obj_t *)"
|
||||
extern "lv_obj_user_data_t lv_obj_get_user_data(const lv_obj_t *)"
|
||||
extern "lv_obj_user_data_t * lv_obj_get_user_data_ptr(const lv_obj_t *)"
|
||||
extern "void lv_obj_set_user_data(lv_obj_t *, lv_obj_user_data_t)"
|
||||
extern "void lv_obj_set_event_cb(lv_obj_t *, lv_event_cb_t)"
|
||||
extern "const void *lv_event_get_data()"
|
||||
extern "void lv_obj_set_opa_scale(lv_obj_t *, lv_opa_t)"
|
||||
extern "lv_opa_t lv_obj_get_opa_scale(const lv_obj_t *)"
|
||||
extern "void lv_obj_set_pos(lv_obj_t *, lv_coord_t, lv_coord_t)"
|
||||
extern "void lv_obj_set_x(lv_obj_t *, lv_coord_t)"
|
||||
extern "void lv_obj_set_y(lv_obj_t *, lv_coord_t)"
|
||||
extern "void lv_obj_set_parent(lv_obj_t *, lv_obj_t *)"
|
||||
extern "void lv_obj_set_hidden(lv_obj_t *, bool)"
|
||||
extern "void lv_obj_set_click(lv_obj_t *, bool)"
|
||||
extern "void lv_obj_set_top(lv_obj_t *, bool)"
|
||||
extern "void lv_obj_set_opa_scale_enable(lv_obj_t *, bool)"
|
||||
extern "lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t *)"
|
||||
extern "void lv_obj_clean(lv_obj_t *)"
|
||||
extern "lv_res_t lv_obj_del(lv_obj_t *)"
|
||||
extern "void lv_obj_del_async(struct _lv_obj_t *)"
|
||||
extern "lv_obj_t * lv_obj_get_parent(const lv_obj_t *)"
|
||||
extern "bool lv_obj_is_children(const lv_obj_t * obj, const lv_obj_t * target)"
|
||||
extern "lv_obj_t *lv_obj_get_child_back(const lv_obj_t *, const lv_obj_t *)"
|
||||
|
||||
def handle_lv_event(obj_p, event)
|
||||
#userdata = lv_obj_get_user_data(obj_p)
|
||||
#instance = userdata.to_value
|
||||
# Pick from our registry, until we can rehydrate the object type with Fiddle.
|
||||
instance = LVGL::LVObject::REGISTRY[obj_p.to_i]
|
||||
instance.instance_exec do
|
||||
if @event_handler_proc
|
||||
@event_handler_proc.call(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
bound_method! :handle_lv_event, "void handle_lv_event_(struct _lv_obj_t *, lv_event_t)"
|
||||
|
||||
# lvgl/src/lv_objx/lv_btn.h
|
||||
extern "lv_obj_t * lv_btn_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_btn_set_ink_in_time(lv_obj_t *, uint16_t)"
|
||||
extern "void lv_btn_set_ink_wait_time(lv_obj_t *, uint16_t)"
|
||||
extern "void lv_btn_set_ink_out_time(lv_obj_t *, uint16_t)"
|
||||
|
||||
# lvgl/src/lv_objx/lv_cont.h
|
||||
#typedef uint8_t lv_layout_t;
|
||||
enum!(:LV_LAYOUT, [
|
||||
{OFF: 0}, #< No layout */
|
||||
:CENTER, #< Center objects */
|
||||
:COL_L, #< Column left align*/
|
||||
:COL_M, #< Column middle align*/
|
||||
:COL_R, #< Column right align*/
|
||||
:ROW_T, #< Row top align*/
|
||||
:ROW_M, #< Row middle align*/
|
||||
:ROW_B, #< Row bottom align*/
|
||||
:PRETTY, #< Put as many object as possible in row and begin a new row*/
|
||||
:GRID, #< Align same-sized object into a grid*/
|
||||
], type: "uint8_t")
|
||||
typealias("lv_layout_t", "LV_LAYOUT")
|
||||
|
||||
enum!(:LV_FIT, [
|
||||
:NONE, #< Do not change the size automatically*/
|
||||
:TIGHT, #< Shrink wrap around the children */
|
||||
:FLOOD, #< Align the size to the parent's edge*/
|
||||
:FILL, #< Align the size to the parent's edge first but if there is an object out of it
|
||||
# then get larger */
|
||||
], type: "uint8_t")
|
||||
typealias("lv_fit_t", "LV_FIT")
|
||||
|
||||
# typedef uint8_t lv_cont_style_t;
|
||||
enum!(:LV_CONT_STYLE, [
|
||||
:MAIN,
|
||||
])
|
||||
typealias("lv_cont_style_t", "LV_CONT_STYLE")
|
||||
|
||||
extern "lv_obj_t * lv_cont_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_cont_set_layout(lv_obj_t *, lv_layout_t)"
|
||||
extern "void lv_cont_set_fit4(lv_obj_t *, lv_fit_t, lv_fit_t, lv_fit_t, lv_fit_t)"
|
||||
extern "void lv_cont_set_fit2(lv_obj_t *, lv_fit_t, lv_fit_t)"
|
||||
extern "void lv_cont_set_fit(lv_obj_t *, lv_fit_t)"
|
||||
|
||||
# lvgl/src/lv_core/lv_disp.h
|
||||
extern "lv_obj_t *lv_disp_get_scr_act(lv_disp_t *)"
|
||||
extern "void lv_disp_load_scr(lv_obj_t *)"
|
||||
extern "lv_disp_t *lv_disp_get_default()"
|
||||
extern "lv_obj_t *lv_scr_act()"
|
||||
|
||||
# lvgl/src/lv_objx/lv_img.h
|
||||
extern "lv_obj_t * lv_img_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_img_set_src(lv_obj_t *, const void *)"
|
||||
|
||||
# lvgl/src/lv_objx/lv_label.h
|
||||
enum!(:LV_LABEL_LONG, [
|
||||
:EXPAND, #< Expand the object size to the text size*/
|
||||
:BREAK, #< Keep the object width, break the too long lines and expand the object
|
||||
# height*/
|
||||
:DOT, #< Keep the size and write dots at the end if the text is too long*/
|
||||
:SROLL, #< Keep the size and roll the text back and forth*/
|
||||
:SROLL_CIRC, #< Keep the size and roll the text circularly*/
|
||||
:CROP, #< Keep the size and crop the text out of it*/
|
||||
], type: "uint8_t")
|
||||
typealias("lv_label_long_mode_t", "LV_LABEL_LONG")
|
||||
|
||||
enum!(:LV_LABEL_ALIGN, [
|
||||
:LEFT, #< Align text to left */
|
||||
:CENTER, #< Align text to center */
|
||||
:RIGHT, #< Align text to right */
|
||||
:AUTO, #< Use LEFT or RIGHT depending on the direction of the text (LTR/RTL)*/
|
||||
], type: "uint8_t")
|
||||
typealias("lv_label_align_t", "LV_LABEL_ALIGN")
|
||||
|
||||
enum!(:LV_LABEL_STYLE, [
|
||||
:MAIN
|
||||
], type: "uint8_t")
|
||||
typealias("lv_label_style_t", "LV_LABEL_STYLE")
|
||||
|
||||
extern "lv_obj_t * lv_label_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_label_set_text(lv_obj_t *, const char *)"
|
||||
# extern "void lv_label_set_text_fmt(lv_obj_t * label, const char * fmt, ...)" varargs?
|
||||
extern "void lv_label_set_long_mode(lv_obj_t *, lv_label_long_mode_t)"
|
||||
extern "void lv_label_set_align(lv_obj_t *, lv_label_align_t)"
|
||||
|
||||
# lvgl/src/lv_objx/lv_page.h
|
||||
enum!(:LV_ANIM, [
|
||||
:OFF,
|
||||
:ON,
|
||||
])
|
||||
typealias("lv_anim_enable_t", "LV_ANIM")
|
||||
enum!(:LV_PAGE_STYLE, [
|
||||
:BG,
|
||||
:SCRL,
|
||||
:SB,
|
||||
:EDGE_FLASH,
|
||||
], type: "uint8_t")
|
||||
typealias("lv_page_style_t", "LV_PAGE_STYLE")
|
||||
|
||||
enum!(:LV_SB_MODE, [
|
||||
{ :OFF => 0x0 },
|
||||
{ :ON => 0x1 },
|
||||
{ :DRAG => 0x2 },
|
||||
{ :AUTO => 0x3 },
|
||||
{ :HIDE => 0x4 },
|
||||
{ :UNHIDE => 0x5 },
|
||||
], type: "uint8_t")
|
||||
typealias("lv_sb_mode_t", "LV_SB_MODE")
|
||||
|
||||
extern "lv_obj_t * lv_page_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_page_clean(lv_obj_t *)"
|
||||
extern "lv_obj_t * lv_page_get_scrl(const lv_obj_t *)"
|
||||
extern "void lv_page_set_scrl_layout(lv_obj_t *, lv_layout_t)"
|
||||
extern "void lv_page_glue_obj(lv_obj_t *, bool)"
|
||||
extern "void lv_page_set_style(lv_obj_t *, lv_page_style_t, const lv_style_t *)"
|
||||
extern "void lv_page_focus(lv_obj_t *, const lv_obj_t *, lv_anim_enable_t)"
|
||||
|
||||
|
||||
# lvgl/src/lv_objx/lv_kb.h
|
||||
enum!(:LV_KB_MODE, [
|
||||
:TEXT,
|
||||
:NUM,
|
||||
:TEXT_UPPER,
|
||||
], type: "uint8_t")
|
||||
typealias("lv_kb_mode_t", "LV_KB_MODE")
|
||||
|
||||
enum!(:LV_KB_STYLE, [
|
||||
:BG,
|
||||
:BTN_REL,
|
||||
:BTN_PR,
|
||||
:BTN_TGL_REL,
|
||||
:BTN_TGL_PR,
|
||||
:BTN_INA,
|
||||
])
|
||||
typealias("lv_kb_style_t", "LV_KB_STYLE")
|
||||
|
||||
extern "lv_obj_t * lv_kb_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_kb_set_ta(lv_obj_t *, lv_obj_t *)"
|
||||
extern "void lv_kb_set_mode(lv_obj_t *, lv_kb_mode_t)"
|
||||
extern "void lv_kb_set_cursor_manage(lv_obj_t *, bool)"
|
||||
extern "void lv_kb_set_map(lv_obj_t *, const char * [])" # ??
|
||||
extern "void lv_kb_set_ctrl_map(lv_obj_t * , const lv_btnm_ctrl_t [])" # ??
|
||||
extern "void lv_kb_set_style(lv_obj_t *, lv_kb_style_t, const lv_style_t *)"
|
||||
extern "lv_obj_t * lv_kb_get_ta(const lv_obj_t *)"
|
||||
extern "lv_kb_mode_t lv_kb_get_mode(const lv_obj_t *)"
|
||||
extern "bool lv_kb_get_cursor_manage(const lv_obj_t *)"
|
||||
extern "const char ** lv_kb_get_map_array(const lv_obj_t *)"
|
||||
extern "const lv_style_t * lv_kb_get_style(const lv_obj_t *, lv_kb_style_t)"
|
||||
extern "void lv_kb_def_event_cb(lv_obj_t *, lv_event_t)"
|
||||
|
||||
|
||||
# lvgl/src/lv_objx/lv_ta.h
|
||||
enum!(:LV_CURSOR, [
|
||||
:NONE,
|
||||
:LINE,
|
||||
:BLOCK,
|
||||
:OUTLINE,
|
||||
:UNDERLINE,
|
||||
{ :HIDDEN => 0x08 },
|
||||
], type: "uint8_t")
|
||||
typealias("lv_cursor_type_t", "LV_CURSOR")
|
||||
|
||||
enum!(:LV_TA_STYLE, [
|
||||
:BG,
|
||||
:SB,
|
||||
:CURSOR,
|
||||
:EDGE_FLASH,
|
||||
:PLACEHOLDER,
|
||||
])
|
||||
typealias("lv_ta_style_t", "LV_TA_STYLE")
|
||||
|
||||
extern "lv_obj_t * lv_ta_create(lv_obj_t *, const lv_obj_t *)"
|
||||
extern "void lv_ta_add_char(lv_obj_t *, uint32_t)"
|
||||
extern "void lv_ta_add_text(lv_obj_t *, const char *)"
|
||||
extern "void lv_ta_del_char(lv_obj_t *)"
|
||||
extern "void lv_ta_del_char_forward(lv_obj_t *)"
|
||||
extern "void lv_ta_set_text(lv_obj_t *, const char *)"
|
||||
extern "void lv_ta_set_placeholder_text(lv_obj_t *, const char *)"
|
||||
extern "void lv_ta_set_cursor_pos(lv_obj_t *, int16_t)"
|
||||
extern "void lv_ta_set_cursor_type(lv_obj_t *, lv_cursor_type_t)"
|
||||
extern "void lv_ta_set_cursor_click_pos(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_pwd_mode(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_one_line(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_text_align(lv_obj_t *, lv_label_align_t)"
|
||||
extern "void lv_ta_set_accepted_chars(lv_obj_t *, const char *)"
|
||||
extern "void lv_ta_set_max_length(lv_obj_t *, uint16_t)"
|
||||
#extern "void lv_ta_set_insert_replace(lv_obj_t *, const char *)"
|
||||
extern "void lv_ta_set_sb_mode(lv_obj_t *, lv_sb_mode_t)"
|
||||
extern "void lv_ta_set_scroll_propagation(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_edge_flash(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_style(lv_obj_t *, lv_ta_style_t, const lv_style_t *)"
|
||||
extern "void lv_ta_set_text_sel(lv_obj_t *, bool)"
|
||||
extern "void lv_ta_set_pwd_show_time(lv_obj_t *, uint16_t)"
|
||||
extern "void lv_ta_set_cursor_blink_time(lv_obj_t *, uint16_t)"
|
||||
extern "const char * lv_ta_get_text(const lv_obj_t *)"
|
||||
extern "const char * lv_ta_get_placeholder_text(lv_obj_t *)"
|
||||
extern "lv_obj_t * lv_ta_get_label(const lv_obj_t *)"
|
||||
extern "uint16_t lv_ta_get_cursor_pos(const lv_obj_t *)"
|
||||
extern "lv_cursor_type_t lv_ta_get_cursor_type(const lv_obj_t *)"
|
||||
extern "bool lv_ta_get_cursor_click_pos(lv_obj_t *)"
|
||||
extern "bool lv_ta_get_pwd_mode(const lv_obj_t *)"
|
||||
extern "bool lv_ta_get_one_line(const lv_obj_t *)"
|
||||
extern "const char * lv_ta_get_accepted_chars(lv_obj_t *)"
|
||||
extern "uint16_t lv_ta_get_max_length(lv_obj_t *)"
|
||||
extern "lv_sb_mode_t lv_ta_get_sb_mode(const lv_obj_t *)"
|
||||
extern "bool lv_ta_get_scroll_propagation(lv_obj_t *)"
|
||||
extern "bool lv_ta_get_edge_flash(lv_obj_t *)"
|
||||
extern "const lv_style_t * lv_ta_get_style(const lv_obj_t *, lv_ta_style_t)"
|
||||
extern "bool lv_ta_text_is_selected(const lv_obj_t *)"
|
||||
extern "bool lv_ta_get_text_sel_en(lv_obj_t *)"
|
||||
extern "uint16_t lv_ta_get_pwd_show_time(lv_obj_t *)"
|
||||
extern "uint16_t lv_ta_get_cursor_blink_time(lv_obj_t *)"
|
||||
extern "void lv_ta_clear_selection(lv_obj_t *)"
|
||||
extern "void lv_ta_cursor_right(lv_obj_t *)"
|
||||
extern "void lv_ta_cursor_left(lv_obj_t *)"
|
||||
extern "void lv_ta_cursor_down(lv_obj_t *)"
|
||||
extern "void lv_ta_cursor_up(lv_obj_t *)"
|
||||
|
||||
# lvgl/src/lv_core/lv_style.h
|
||||
|
||||
#typedef uint8_t lv_border_part_t
|
||||
enum!(:LV_BORDER, [
|
||||
{ :NONE => 0x00 },
|
||||
{ :BOTTOM => 0x01 },
|
||||
{ :TOP => 0x02 },
|
||||
{ :LEFT => 0x04 },
|
||||
{ :RIGHT => 0x08 },
|
||||
{ :FULL => 0x0F },
|
||||
{ :INTERNAL => 0x10 },
|
||||
], type: :uint8_t)
|
||||
typealias("lv_border_part_t", "LV_BORDER")
|
||||
|
||||
enum!(:LV_SHADOW, [
|
||||
:BOTTOM,
|
||||
:FULL,
|
||||
], type: :uint8_t)
|
||||
typealias("lv_shadow_type_t", "LV_SHADOW")
|
||||
|
||||
#extern "void lv_style_init(void)"
|
||||
extern "void lv_style_copy(lv_style_t *, const lv_style_t *)"
|
||||
|
||||
# Animations
|
||||
typealias "lv_anim_value_t", "int16_t"
|
||||
typedef "lv_anim_exec_xcb_t", "void (*lv_anim_exec_xcb_t)(void *, lv_anim_value_t)"
|
||||
typedef "lv_anim_path_cb_t", "lv_anim_value_t (*lv_anim_path_cb_t)(const struct _lv_anim_t *)"
|
||||
extern "void lv_anim_init(lv_anim_t *)"
|
||||
extern "void lv_anim_set_exec_cb(lv_anim_t *, void *, lv_anim_exec_xcb_t)"
|
||||
extern "void lv_anim_create(lv_anim_t *)"
|
||||
extern "void lv_anim_del(lv_anim_t *)"
|
||||
extern "void lv_anim_clear_repeat(lv_anim_t *)"
|
||||
extern "void lv_anim_set_repeat(lv_anim_t *, uint16_t)"
|
||||
extern "void lv_anim_set_playback(lv_anim_t *, uint16_t)"
|
||||
extern "void lv_anim_set_time(lv_anim_t *, int16_t, int16_t)"
|
||||
extern "void lv_anim_set_path_cb(lv_anim_t *, lv_anim_path_cb_t)"
|
||||
extern "void lv_anim_set_values(lv_anim_t *, lv_anim_value_t, lv_anim_value_t)"
|
||||
|
||||
# Focus groups
|
||||
typedef "lv_group_focus_cb_t", "void (*lv_group_focus_cb_t)(struct _lv_group_t *)"
|
||||
extern "void lv_anim_core_init()"
|
||||
extern "lv_group_t * lvgui_get_focus_group()"
|
||||
extern "void lvgui_focus_ring_disable()"
|
||||
extern "void lv_group_add_obj(lv_group_t *, lv_obj_t *)"
|
||||
extern "void lv_group_remove_obj(lv_obj_t *)"
|
||||
extern "void lv_group_remove_all_objs(lv_group_t *)"
|
||||
extern "void lv_group_focus_obj(lv_obj_t *)"
|
||||
extern "void lv_group_focus_next(lv_group_t *)"
|
||||
extern "void lv_group_focus_prev(lv_group_t *)"
|
||||
extern "void lv_group_focus_freeze(lv_group_t *, bool)"
|
||||
extern "void lv_group_set_click_focus(lv_group_t *, bool)"
|
||||
extern "void lv_group_set_wrap(lv_group_t *, bool)"
|
||||
extern "lv_obj_t *lv_group_get_focused(const lv_group_t *)"
|
||||
extern "void lv_group_set_focus_cb(lv_group_t *, lv_group_focus_cb_t)"
|
||||
extern "lv_obj_t * lv_group_get_focused(const lv_group_t *)"
|
||||
|
||||
typedef "lv_group_user_data_t", "void *"
|
||||
extern "lv_group_user_data_t *lv_group_get_user_data(lv_group_t *)"
|
||||
extern "void lv_group_set_user_data(lv_group_t *, lv_group_user_data_t)"
|
||||
|
||||
def handle_lv_focus(group_p)
|
||||
#userdata = lv_group_get_user_data(group_p)
|
||||
#instance = userdata.to_value
|
||||
# Pick from our registry, until we can rehydrate the object type with Fiddle.
|
||||
instance = LVGL::LVGroup::REGISTRY[group_p.to_i]
|
||||
instance.instance_exec do
|
||||
if @focus_handler_proc
|
||||
@focus_handler_proc.call()
|
||||
end
|
||||
end
|
||||
end
|
||||
bound_method! :handle_lv_focus, "void handle_lv_focus_(_lv_group_t *)"
|
||||
end
|
97
boot/lib/lvgui/lvgl/generated_ffi.rb
Normal file
97
boot/lib/lvgui/lvgl/generated_ffi.rb
Normal file
@ -0,0 +1,97 @@
|
||||
module LVGL::FFI
|
||||
|
||||
extern "lv_style_t * lvgui_allocate_lv_style()"
|
||||
|
||||
extern "uint8_t lvgui_get_lv_style__glass(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__glass(lv_style_t *, uint8_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__body_main_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_main_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__body_grad_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_grad_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_radius(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_radius(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__body_opa(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_opa(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__body_border_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_border_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_border_width(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_border_width(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_border_part_t lvgui_get_lv_style__body_border_part(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_border_part(lv_style_t *, lv_border_part_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__body_border_opa(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_border_opa(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__body_shadow_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_shadow_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_shadow_width(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_shadow_width(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_shadow_type_t lvgui_get_lv_style__body_shadow_type(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_shadow_type(lv_style_t *, lv_shadow_type_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_padding_top(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_padding_top(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_padding_bottom(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_padding_bottom(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_padding_left(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_padding_left(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_padding_right(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_padding_right(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__body_padding_inner(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__body_padding_inner(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__text_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__text_sel_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_sel_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_font_t * lvgui_get_lv_style__text_font(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_font(lv_style_t *, lv_font_t *)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__text_letter_space(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_letter_space(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__text_line_space(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_line_space(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__text_opa(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__text_opa(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__image_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__image_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__image_intense(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__image_intense(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__image_opa(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__image_opa(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "lv_color_t lvgui_get_lv_style__line_color(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__line_color(lv_style_t *, lv_color_t)"
|
||||
|
||||
extern "lv_coord_t lvgui_get_lv_style__line_width(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__line_width(lv_style_t *, lv_coord_t)"
|
||||
|
||||
extern "lv_opa_t lvgui_get_lv_style__line_opa(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__line_opa(lv_style_t *, lv_opa_t)"
|
||||
|
||||
extern "uint8_t lvgui_get_lv_style__line_rounded(lv_style_t *)"
|
||||
extern "void lvgui_set_lv_style__line_rounded(lv_style_t *, uint8_t)"
|
||||
|
||||
extern "lv_anim_t * lvgui_allocate_lv_anim()"
|
||||
|
||||
end # module LVGL::FFI
|
109
boot/lib/lvgui/lvgl/hacks.rb
Normal file
109
boot/lib/lvgui/lvgl/hacks.rb
Normal file
@ -0,0 +1,109 @@
|
||||
# FFI bindings for a couple of our hacks.
|
||||
module LVGL::FFI
|
||||
# TODO: Figure out when the following struct (and this typedef) are at the
|
||||
# end, this segfaults...
|
||||
typedef "lv_task_cb_t", "void (*lv_task_cb_t)(struct _lv_task_t *)"
|
||||
LvTask = struct! [ # {{{
|
||||
[:uint32_t, :period],
|
||||
[:uint32_t, :last_run],
|
||||
[:lv_task_cb_t, :task_cb],
|
||||
["void *", :user_data],
|
||||
[:uint8_t, :prio],
|
||||
[:uint8_t, :once],
|
||||
] # }}}
|
||||
|
||||
extern "void hal_init()"
|
||||
|
||||
# TODO: begin/rescue DLError and assume failing is we're not in simulator.
|
||||
if lv_introspection_is_simulator
|
||||
global!("int", "monitor_width")
|
||||
global!("int", "monitor_height")
|
||||
end
|
||||
|
||||
extern "void lv_task_handler()"
|
||||
|
||||
def handle_lv_task(lv_task_p)
|
||||
# Unwrap the lv_task struct we received
|
||||
lv_task = LvTask.new(lv_task_p.to_i)
|
||||
# This is the userdata that has been given
|
||||
userdata = lv_task.user_data
|
||||
# Side-step our inability to rehydrate an mruby Object properly
|
||||
task = LVGL::Hacks::LVTask::REGISTRY[userdata.to_i]
|
||||
# Call the task
|
||||
task.call()
|
||||
end
|
||||
bound_method! :handle_lv_task, "void handle_lv_task_(lv_task_t * task)"
|
||||
|
||||
extern "lv_task_t * lv_task_create(lv_task_cb_t, uint32_t, lv_task_prio_t, void *)"
|
||||
end
|
||||
|
||||
# FFI bindings for "hacks" for lv_lib_nanosvg
|
||||
module LVGL::FFI
|
||||
# lv_lib_nanosvg/lv_nanosvg.h
|
||||
extern "void lv_nanosvg_init()"
|
||||
extern "void lv_nanosvg_resize_next_width(int)"
|
||||
extern "void lv_nanosvg_resize_next_height(int)"
|
||||
end
|
||||
|
||||
module LVGL::Hacks
|
||||
def self.init()
|
||||
puts "??1"
|
||||
LVGL::FFI.hal_init()
|
||||
puts "??2"
|
||||
LVGL::FFI.lv_nanosvg_init()
|
||||
puts "??3"
|
||||
end
|
||||
|
||||
def self.monitor_height=(v)
|
||||
if LVGL::Introspection.simulator?
|
||||
LVGL::FFI.monitor_height = v
|
||||
end
|
||||
end
|
||||
def self.monitor_width=(v)
|
||||
if LVGL::Introspection.simulator?
|
||||
LVGL::FFI.monitor_width = v
|
||||
end
|
||||
end
|
||||
def self.theme_night(hue)
|
||||
LVGL::FFI.lv_theme_set_current(
|
||||
LVGL::FFI.lv_theme_night_init(hue, 0)
|
||||
)
|
||||
end
|
||||
|
||||
module LVTask
|
||||
# Temp hack...
|
||||
# I need to figure out how to use Fiddle's #to_value to rehydrate an mruby
|
||||
# Object into its proper form.
|
||||
REGISTRY = {
|
||||
# userdata pointer int value => instance
|
||||
}
|
||||
def self.create_task(period, prio, task)
|
||||
userdata = Fiddle::Pointer[task]
|
||||
REGISTRY[userdata.to_i] = task
|
||||
|
||||
LVGL::FFI.lv_task_create(
|
||||
LVGL::FFI["handle_lv_task"],
|
||||
period,
|
||||
prio,
|
||||
userdata
|
||||
)
|
||||
end
|
||||
|
||||
def self.handle_tasks()
|
||||
#$stderr.puts "-> handle_tasks"
|
||||
LVGL::FFI.lv_task_handler()
|
||||
#$stderr.puts "<- handle_tasks"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module LVGL::Hacks
|
||||
module LVNanoSVG
|
||||
def self.resize_next_width(width)
|
||||
LVGL::FFI.lv_nanosvg_resize_next_width(width)
|
||||
end
|
||||
def self.resize_next_height(height)
|
||||
LVGL::FFI.lv_nanosvg_resize_next_height(height)
|
||||
end
|
||||
end
|
||||
end
|
11
boot/lib/lvgui/lvgl/introspection.rb
Normal file
11
boot/lib/lvgui/lvgl/introspection.rb
Normal file
@ -0,0 +1,11 @@
|
||||
module LVGL::Introspection
|
||||
def self.debug?()
|
||||
LVGL::FFI.lv_introspection_is_debug != 0
|
||||
end
|
||||
def self.simulator?()
|
||||
LVGL::FFI.lv_introspection_is_simulator != 0
|
||||
end
|
||||
def self.use_assert_style?()
|
||||
LVGL::FFI.lv_introspection_use_assert_style != 0
|
||||
end
|
||||
end
|
579
boot/lib/lvgui/lvgl/lvgl.rb
Normal file
579
boot/lib/lvgui/lvgl/lvgl.rb
Normal file
@ -0,0 +1,579 @@
|
||||
module LVGL
|
||||
[
|
||||
:ANIM,
|
||||
:CONT_STYLE,
|
||||
:CURSOR,
|
||||
:EVENT,
|
||||
:FIT,
|
||||
:KB_MODE,
|
||||
:KB_STYLE,
|
||||
:LABEL_ALIGN,
|
||||
:LABEL_LONG,
|
||||
:LABEL_STYLE,
|
||||
:LAYOUT,
|
||||
:PAGE_STYLE,
|
||||
:TASK_PRIO,
|
||||
:TA_STYLE,
|
||||
].each do |enum_name|
|
||||
const_set(enum_name, LVGL::FFI.const_get("LV_#{enum_name}".to_sym))
|
||||
LVGL.const_get(enum_name).module_exec do
|
||||
def self.from_value(needle)
|
||||
self.constants.find do |name|
|
||||
needle == self.const_get(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.ffi_call!(klass, meth, *args, _initiator_class: nil)
|
||||
_initiator_class ||= klass
|
||||
unless klass.const_defined?(:LV_TYPE)
|
||||
raise "Tried to ffi_call!(..., #{meth}) with a #{_initiator_class.name}, which does not define LV_TYPE"
|
||||
end
|
||||
|
||||
ffi_name = "lv_#{klass.const_get(:LV_TYPE)}_#{meth}".to_sym
|
||||
if LVGL::FFI.respond_to?(ffi_name)
|
||||
args = args.map do |arg|
|
||||
case arg
|
||||
when nil
|
||||
0
|
||||
when false
|
||||
0
|
||||
when true
|
||||
1
|
||||
else
|
||||
if arg.respond_to? :lv_obj_pointer
|
||||
arg.lv_obj_pointer
|
||||
else
|
||||
arg
|
||||
end
|
||||
end
|
||||
end
|
||||
return LVGL::FFI.send(ffi_name, *args)
|
||||
else
|
||||
if klass.superclass
|
||||
return ffi_call!(klass.superclass, meth, *args, _initiator_class: _initiator_class)
|
||||
else
|
||||
raise "Could not find #{meth} in the class hierarchy."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class LVDisplay
|
||||
# This is not actually an object type in LVGL proper.
|
||||
|
||||
# Get default display
|
||||
def self.get_default()
|
||||
LVGL::FFI.lv_disp_get_default()
|
||||
end
|
||||
|
||||
# Gets the current active screen.
|
||||
def self.get_scr_act()
|
||||
LVObject.from_pointer(
|
||||
LVGL::FFI.lv_disp_get_scr_act(get_default())
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
class LVObject
|
||||
LV_TYPE = :obj
|
||||
|
||||
# Hack...
|
||||
# I need to figure out how to use Fiddle's #to_value to rehydrate an mruby
|
||||
# Object into its proper form.
|
||||
REGISTRY = {
|
||||
# @self_pointer int value => instance
|
||||
}
|
||||
|
||||
def initialize(parent = nil, pointer: nil)
|
||||
@event_handler_proc = nil
|
||||
|
||||
unless pointer
|
||||
parent_ptr =
|
||||
if parent
|
||||
parent.lv_obj_pointer
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
@self_pointer = LVGL.ffi_call!(self.class, :create, parent_ptr, nil)
|
||||
else
|
||||
@self_pointer = pointer
|
||||
end
|
||||
register_userdata
|
||||
unless parent or pointer
|
||||
$stderr.puts("[HACK] Creating #{self.class.name} as screen. (Switching lv_disp_load_scr!)")
|
||||
LVGL::FFI.lv_disp_load_scr(@self_pointer)
|
||||
end
|
||||
end
|
||||
|
||||
def lv_obj_pointer()
|
||||
@self_pointer
|
||||
end
|
||||
|
||||
def self.from_pointer(pointer)
|
||||
if REGISTRY[pointer.to_i]
|
||||
REGISTRY[pointer.to_i]
|
||||
else
|
||||
self.new(pointer: pointer)
|
||||
end
|
||||
end
|
||||
|
||||
def get_style()
|
||||
style = LVGL.ffi_call!(self.class, :get_style, @self_pointer)
|
||||
LVGL::LVStyle.from_pointer(style)
|
||||
end
|
||||
|
||||
def set_style(style)
|
||||
# Prevents the object from being collected
|
||||
@style = style
|
||||
LVGL.ffi_call!(self.class, :set_style, @self_pointer, style.lv_style_pointer)
|
||||
end
|
||||
|
||||
def glue_obj(value)
|
||||
value =
|
||||
if value
|
||||
1
|
||||
else
|
||||
0
|
||||
end
|
||||
LVGL::FFI.lv_page_glue_obj(@self_pointer, value)
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
LVGL.ffi_call!(self.class, meth, @self_pointer, *args)
|
||||
end
|
||||
|
||||
def event_handler=(cb_proc)
|
||||
# Hook the handler on-the-fly.
|
||||
unless @event_handler_proc
|
||||
LVGL.ffi_call!(self.class, :set_event_cb, @self_pointer, LVGL::FFI["handle_lv_event"])
|
||||
end
|
||||
@event_handler_proc = cb_proc
|
||||
end
|
||||
|
||||
def register_userdata()
|
||||
userdata = Fiddle::Pointer[self]
|
||||
REGISTRY[@self_pointer.to_i] = self
|
||||
LVGL.ffi_call!(self.class, :set_user_data, @self_pointer, userdata)
|
||||
end
|
||||
|
||||
def get_parent()
|
||||
ptr = LVGL.ffi_call!(self.class, :get_parent, @self_pointer)
|
||||
LVObject.from_pointer(ptr)
|
||||
end
|
||||
|
||||
def get_children()
|
||||
children = []
|
||||
last_child = nil
|
||||
loop do
|
||||
ptr = LVGL.ffi_call!(self.class, :get_child_back, @self_pointer, last_child)
|
||||
break if ptr.null?
|
||||
last_child = LVObject.from_pointer(ptr)
|
||||
children << last_child
|
||||
end
|
||||
children
|
||||
end
|
||||
end
|
||||
|
||||
class LVContainer < LVObject
|
||||
LV_TYPE = :cont
|
||||
|
||||
def set_layout(*args)
|
||||
LVGL::FFI.lv_cont_set_layout(@self_pointer, *args)
|
||||
end
|
||||
|
||||
def get_style(type)
|
||||
# type is unused, see lvgl/src/lv_objx/lv_cont.h
|
||||
super()
|
||||
end
|
||||
|
||||
def set_style(type, style)
|
||||
# type is unused, see lvgl/src/lv_objx/lv_cont.h
|
||||
super(style)
|
||||
end
|
||||
end
|
||||
|
||||
class LVLabel < LVObject
|
||||
LV_TYPE = :label
|
||||
|
||||
def get_style(type)
|
||||
# type is unused, see lvgl/src/lv_objx/lv_label.h
|
||||
super()
|
||||
end
|
||||
|
||||
def set_style(type, style)
|
||||
# type is unused, see lvgl/src/lv_objx/lv_label.h
|
||||
super(style)
|
||||
end
|
||||
|
||||
def set_text(text)
|
||||
# The "\0" thing is a bit scary; it seems that *something* related
|
||||
# to C string and "\0" in either mruby or LVGL, likely mruby, may
|
||||
# cause issues when using something like `split` to split a bigger
|
||||
# string.
|
||||
#
|
||||
# My assumption is that the ruby string is not \0 completed, and
|
||||
# given as-is to the C world via ffi.
|
||||
LVGL.ffi_call!(self.class, :set_text, @self_pointer, text + "\0")
|
||||
end
|
||||
end
|
||||
|
||||
class LVImage < LVObject
|
||||
LV_TYPE = :img
|
||||
end
|
||||
|
||||
class LVPage < LVContainer
|
||||
LV_TYPE = :page
|
||||
|
||||
def set_style(type, style)
|
||||
# Prevents the object from being collected
|
||||
@style = style
|
||||
LVGL.ffi_call!(self.class, :set_style, @self_pointer, type, style.lv_style_pointer)
|
||||
end
|
||||
|
||||
def focus(obj, anim)
|
||||
ptr =
|
||||
if obj.respond_to?(:lv_obj_pointer)
|
||||
obj.lv_obj_pointer
|
||||
else
|
||||
obj
|
||||
end
|
||||
LVGL.ffi_call!(self.class, :focus, @self_pointer, ptr, anim)
|
||||
end
|
||||
end
|
||||
|
||||
class LVButton < LVContainer
|
||||
LV_TYPE = :btn
|
||||
end
|
||||
|
||||
class LVTextArea < LVObject
|
||||
LV_TYPE = :ta
|
||||
|
||||
def add_text(text)
|
||||
# The "\0" thing is a bit scary; it seems that *something* related
|
||||
# to C string and "\0" in either mruby or LVGL, likely mruby, may
|
||||
# cause issues when using something like `split` to split a bigger
|
||||
# string.
|
||||
#
|
||||
# My assumption is that the ruby string is not \0 completed, and
|
||||
# given as-is to the C world via ffi.
|
||||
LVGL.ffi_call!(self.class, :add_text, @self_pointer, text + "\0")
|
||||
end
|
||||
|
||||
def set_text(text)
|
||||
# The "\0" thing is a bit scary; it seems that *something* related
|
||||
# to C string and "\0" in either mruby or LVGL, likely mruby, may
|
||||
# cause issues when using something like `split` to split a bigger
|
||||
# string.
|
||||
#
|
||||
# My assumption is that the ruby string is not \0 completed, and
|
||||
# given as-is to the C world via ffi.
|
||||
LVGL.ffi_call!(self.class, :set_text, @self_pointer, text + "\0")
|
||||
end
|
||||
|
||||
def set_placeholder_text(text)
|
||||
# The "\0" thing is a bit scary; it seems that *something* related
|
||||
# to C string and "\0" in either mruby or LVGL, likely mruby, may
|
||||
# cause issues when using something like `split` to split a bigger
|
||||
# string.
|
||||
#
|
||||
# My assumption is that the ruby string is not \0 completed, and
|
||||
# given as-is to the C world via ffi.
|
||||
LVGL.ffi_call!(self.class, :set_placeholder_text, @self_pointer, text + "\0")
|
||||
end
|
||||
|
||||
def set_accepted_chars(text)
|
||||
# The "\0" thing is a bit scary; it seems that *something* related
|
||||
# to C string and "\0" in either mruby or LVGL, likely mruby, may
|
||||
# cause issues when using something like `split` to split a bigger
|
||||
# string.
|
||||
#
|
||||
# My assumption is that the ruby string is not \0 completed, and
|
||||
# given as-is to the C world via ffi.
|
||||
LVGL.ffi_call!(self.class, :set_accepted_chars, @self_pointer, text + "\0")
|
||||
end
|
||||
|
||||
def get_style(style_type)
|
||||
style = LVGL.ffi_call!(self.class, :get_style, @self_pointer, style_type)
|
||||
LVGL::LVStyle.from_pointer(style)
|
||||
end
|
||||
|
||||
def set_style(style_type, style)
|
||||
# Prevents the object from being collected
|
||||
@_style ||= {}
|
||||
@_style[style_type] = style
|
||||
LVGL.ffi_call!(self.class, :set_style, @self_pointer, style_type, style.lv_style_pointer)
|
||||
end
|
||||
end
|
||||
|
||||
class LVKeyboard < LVObject
|
||||
LV_TYPE = :kb
|
||||
|
||||
def get_style(style_type)
|
||||
style = LVGL.ffi_call!(self.class, :get_style, @self_pointer, style_type)
|
||||
LVGL::LVStyle.from_pointer(style)
|
||||
end
|
||||
|
||||
def set_style(style_type, style)
|
||||
# Prevents the object from being collected
|
||||
@_style ||= {}
|
||||
@_style[style_type] = style
|
||||
LVGL.ffi_call!(self.class, :set_style, @self_pointer, style_type, style.lv_style_pointer)
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps an +lv_style_t+ in a class with some light duty housekeeping.
|
||||
class LVStyle
|
||||
# Given a +Fiddle::Pointer+ pointing to an +lv_style_t+, instantiates
|
||||
# an LVStyle class, wrapping the struct.
|
||||
def self.from_pointer(pointer)
|
||||
instance = LVGL::LVStyle.new()
|
||||
instance.instance_exec do
|
||||
@self_pointer = pointer
|
||||
end
|
||||
|
||||
instance
|
||||
end
|
||||
|
||||
# Allocates a new +lv_style_t+, and copies the styles using the LVGL
|
||||
# +lv_style_copy+.
|
||||
def initialize_copy(orig)
|
||||
@self_pointer = LVGL::FFI.lvgui_allocate_lv_style()
|
||||
LVGL::FFI.lv_style_copy(@self_pointer, orig.lv_style_pointer)
|
||||
end
|
||||
|
||||
def lv_style_pointer()
|
||||
@self_pointer
|
||||
end
|
||||
|
||||
# Proxy all methods to the struct accessors we are wrapping.
|
||||
# It's dumb, but it works so well!
|
||||
def method_missing(meth, *args)
|
||||
meth =
|
||||
if meth.to_s.match(/=$/)
|
||||
"lvgui_set_lv_style__#{meth.to_s[0..-2]}".to_sym
|
||||
else
|
||||
"lvgui_get_lv_style__#{meth}".to_sym
|
||||
end
|
||||
|
||||
LVGL::FFI.send(meth, @self_pointer, *args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize()
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
# Initializes global styles
|
||||
[
|
||||
"scr",
|
||||
"transp",
|
||||
"transp_tight",
|
||||
"transp_fit",
|
||||
"plain",
|
||||
"plain_color",
|
||||
"pretty",
|
||||
"pretty_color",
|
||||
"btn_rel",
|
||||
"btn_pr",
|
||||
"btn_tgl_rel",
|
||||
"btn_tgl_pr",
|
||||
"btn_ina",
|
||||
].each do |name|
|
||||
global_name = "lv_style_#{name}".downcase
|
||||
const_name = "style_#{name}".upcase.to_sym
|
||||
wrapped = self.from_pointer(
|
||||
LVGL::FFI.handler.sym(global_name)
|
||||
)
|
||||
const_set(const_name, wrapped)
|
||||
end
|
||||
end
|
||||
|
||||
class LVGroup
|
||||
LV_TYPE = :group
|
||||
|
||||
REGISTRY = {
|
||||
# @self_pointer int value => instance
|
||||
}
|
||||
|
||||
def initialize(pointer: nil)
|
||||
@focus_handler_proc = nil
|
||||
|
||||
unless pointer
|
||||
raise "(FIXME) Creating a focus group is not implemented"
|
||||
#@self_pointer = LVGL.ffi_call!(self.class, :create)
|
||||
else
|
||||
@self_pointer = pointer
|
||||
end
|
||||
register_userdata
|
||||
end
|
||||
|
||||
# Given a +Fiddle::Pointer+ pointing to an +lv_group_t+, instantiates
|
||||
# an LVGroup class, wrapping the struct.
|
||||
def self.from_pointer(pointer)
|
||||
if REGISTRY[pointer.to_i]
|
||||
REGISTRY[pointer.to_i]
|
||||
else
|
||||
self.new(pointer: pointer)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_copy(orig)
|
||||
raise "Not implemented"
|
||||
end
|
||||
|
||||
def lv_group_pointer()
|
||||
@self_pointer
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
LVGL.ffi_call!(self.class, meth, @self_pointer, *args)
|
||||
end
|
||||
|
||||
def add_obj(obj)
|
||||
ptr =
|
||||
if obj.respond_to?(:lv_obj_pointer)
|
||||
obj.lv_obj_pointer
|
||||
else
|
||||
obj
|
||||
end
|
||||
LVGL.ffi_call!(self.class, :add_obj, @self_pointer, ptr)
|
||||
end
|
||||
|
||||
def get_focused()
|
||||
LVObject.from_pointer(
|
||||
LVGL.ffi_call!(self.class, :get_focused, @self_pointer)
|
||||
)
|
||||
end
|
||||
|
||||
def focus_handler=(cb_proc)
|
||||
# Hook the handler on-the-fly.
|
||||
unless @focus_handler
|
||||
LVGL.ffi_call!(self.class, :set_focus_cb, @self_pointer, LVGL::FFI["handle_lv_focus"])
|
||||
end
|
||||
@focus_handler_proc = cb_proc
|
||||
end
|
||||
|
||||
def register_userdata()
|
||||
userdata = Fiddle::Pointer[self]
|
||||
REGISTRY[@self_pointer.to_i] = self
|
||||
LVGL.ffi_call!(self.class, :set_user_data, @self_pointer, userdata)
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps an +lv_anim_t+ in a class with some light duty housekeeping.
|
||||
class LVAnim
|
||||
LV_TYPE = :anim
|
||||
|
||||
# Given a +Fiddle::Pointer+ pointing to an +lv_anim_t+, instantiates
|
||||
# an LVAnim class, wrapping the struct.
|
||||
def self.from_pointer(pointer)
|
||||
instance = LVGL::LVAnim.new()
|
||||
instance.instance_exec do
|
||||
@self_pointer = pointer
|
||||
end
|
||||
|
||||
instance
|
||||
end
|
||||
|
||||
def initialize()
|
||||
@self_pointer = LVGL::FFI.lvgui_allocate_lv_anim()
|
||||
self.init
|
||||
end
|
||||
|
||||
def lv_anim_pointer()
|
||||
@self_pointer
|
||||
end
|
||||
|
||||
def set_exec_cb(obj, cb_name)
|
||||
fn = LVGL::FFI[cb_name.to_s]
|
||||
raise "No function for #{cb_name} on LVGL::FFI" unless fn
|
||||
LVGL.ffi_call!(self.class, "set_exec_cb", @self_pointer, obj.lv_obj_pointer, fn)
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
LVGL.ffi_call!(self.class, meth, @self_pointer, *args)
|
||||
end
|
||||
|
||||
module Path
|
||||
# Initializes global animation paths
|
||||
[
|
||||
"linear",
|
||||
"step",
|
||||
"ease_in",
|
||||
"ease_out",
|
||||
"ease_in_out",
|
||||
"overshoot",
|
||||
"bounce",
|
||||
].each do |name|
|
||||
const_set(
|
||||
name.upcase.to_sym,
|
||||
LVGL::FFI.handler.sym("lv_anim_path_#{name}".downcase)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Symbols
|
||||
AUDIO = "\xef\x80\x81" # 61441, 0xF001
|
||||
VIDEO = "\xef\x80\x88" # 61448, 0xF008
|
||||
LIST = "\xef\x80\x8b" # 61451, 0xF00B
|
||||
OK = "\xef\x80\x8c" # 61452, 0xF00C
|
||||
CLOSE = "\xef\x80\x8d" # 61453, 0xF00D
|
||||
POWER = "\xef\x80\x91" # 61457, 0xF011
|
||||
SETTINGS = "\xef\x80\x93" # 61459, 0xF013
|
||||
HOME = "\xef\x80\x95" # 61461, 0xF015
|
||||
DOWNLOAD = "\xef\x80\x99" # 61465, 0xF019
|
||||
DRIVE = "\xef\x80\x9c" # 61468, 0xF01C
|
||||
REFRESH = "\xef\x80\xa1" # 61473, 0xF021
|
||||
MUTE = "\xef\x80\xa6" # 61478, 0xF026
|
||||
VOLUME_MID = "\xef\x80\xa7" # 61479, 0xF027
|
||||
VOLUME_MAX = "\xef\x80\xa8" # 61480, 0xF028
|
||||
IMAGE = "\xef\x80\xbe" # 61502, 0xF03E
|
||||
EDIT = "\xef\x8C\x84" # 62212, 0xF304
|
||||
PREV = "\xef\x81\x88" # 61512, 0xF048
|
||||
PLAY = "\xef\x81\x8b" # 61515, 0xF04B
|
||||
PAUSE = "\xef\x81\x8c" # 61516, 0xF04C
|
||||
STOP = "\xef\x81\x8d" # 61517, 0xF04D
|
||||
NEXT = "\xef\x81\x91" # 61521, 0xF051
|
||||
EJECT = "\xef\x81\x92" # 61522, 0xF052
|
||||
LEFT = "\xef\x81\x93" # 61523, 0xF053
|
||||
RIGHT = "\xef\x81\x94" # 61524, 0xF054
|
||||
PLUS = "\xef\x81\xa7" # 61543, 0xF067
|
||||
MINUS = "\xef\x81\xa8" # 61544, 0xF068
|
||||
EYE_OPEN = "\xef\x81\xae" # 61550, 0xF06E
|
||||
EYE_CLOSE = "\xef\x81\xb0" # 61552, 0xF070
|
||||
WARNING = "\xef\x81\xb1" # 61553, 0xF071
|
||||
SHUFFLE = "\xef\x81\xb4" # 61556, 0xF074
|
||||
UP = "\xef\x81\xb7" # 61559, 0xF077
|
||||
DOWN = "\xef\x81\xb8" # 61560, 0xF078
|
||||
LOOP = "\xef\x81\xb9" # 61561, 0xF079
|
||||
DIRECTORY = "\xef\x81\xbb" # 61563, 0xF07B
|
||||
UPLOAD = "\xef\x82\x93" # 61587, 0xF093
|
||||
CALL = "\xef\x82\x95" # 61589, 0xF095
|
||||
CUT = "\xef\x83\x84" # 61636, 0xF0C4
|
||||
COPY = "\xef\x83\x85" # 61637, 0xF0C5
|
||||
SAVE = "\xef\x83\x87" # 61639, 0xF0C7
|
||||
CHARGE = "\xef\x83\xa7" # 61671, 0xF0E7
|
||||
PASTE = "\xef\x83\xAA" # 61674, 0xF0EA
|
||||
BELL = "\xef\x83\xb3" # 61683, 0xF0F3
|
||||
KEYBOARD = "\xef\x84\x9c" # 61724, 0xF11C
|
||||
GPS = "\xef\x84\xa4" # 61732, 0xF124
|
||||
FILE = "\xef\x85\x9b" # 61787, 0xF158
|
||||
WIFI = "\xef\x87\xab" # 61931, 0xF1EB
|
||||
BATTERY_FULL = "\xef\x89\x80" # 62016, 0xF240
|
||||
BATTERY_3 = "\xef\x89\x81" # 62017, 0xF241
|
||||
BATTERY_2 = "\xef\x89\x82" # 62018, 0xF242
|
||||
BATTERY_1 = "\xef\x89\x83" # 62019, 0xF243
|
||||
BATTERY_EMPTY = "\xef\x89\x84" # 62020, 0xF244
|
||||
USB = "\xef\x8a\x87" # 62087, 0xF287
|
||||
BLUETOOTH = "\xef\x8a\x93" # 62099, 0xF293
|
||||
TRASH = "\xef\x8B\xAD" # 62189, 0xF2ED
|
||||
BACKSPACE = "\xef\x95\x9A" # 62810, 0xF55A
|
||||
SD_CARD = "\xef\x9F\x82" # 63426, 0xF7C2
|
||||
NEW_LINE = "\xef\xA2\xA2" # 63650, 0xF8A2
|
||||
end
|
||||
end
|
2
boot/lib/lvgui/lvgui/__init.rb
Normal file
2
boot/lib/lvgui/lvgui/__init.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module LVGUI
|
||||
end
|
73
boot/lib/lvgui/lvgui/base_window.rb
Normal file
73
boot/lib/lvgui/lvgui/base_window.rb
Normal file
@ -0,0 +1,73 @@
|
||||
module LVGUI
|
||||
# Extend this to make a "window"
|
||||
class BaseWindow
|
||||
include ::Singleton
|
||||
|
||||
def self.inherited(superclass)
|
||||
superclass.class_eval do
|
||||
unless self.class_variable_defined?(:@@_after_initialize_callback)
|
||||
self.class_variable_set(:@@_after_initialize_callback, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize()
|
||||
super()
|
||||
# Initializes LVGUI things if required...
|
||||
LVGUI.init
|
||||
|
||||
# Preps a basic display
|
||||
@screen = Screen.new()
|
||||
@header = Header.new(@screen)
|
||||
@toolbar = Toolbar.new(@screen)
|
||||
@container = Page.new(@screen)
|
||||
|
||||
@focus_group = []
|
||||
# Dummy object used as a "null" focus
|
||||
LVGL::LVObject.new(@screen).tap do |obj|
|
||||
add_to_focus_group(obj)
|
||||
end
|
||||
reset_focus_group
|
||||
|
||||
self.class.class_variable_get(:@@_after_initialize_callback).each do |cb|
|
||||
instance_eval &cb
|
||||
end
|
||||
end
|
||||
|
||||
# Adds an object to the focus group list, and add it to the
|
||||
# current focus group.
|
||||
def add_to_focus_group(obj)
|
||||
@focus_group << obj
|
||||
LVGUI.focus_group.add_obj(obj)
|
||||
end
|
||||
|
||||
# Re-build the focus group from the elements on the window.
|
||||
def reset_focus_group()
|
||||
# Clear the focus group
|
||||
LVGUI.focus_group.remove_all_objs()
|
||||
|
||||
LVGUI.focus_group.focus_handler = ->() do
|
||||
@container.focus(
|
||||
LVGUI.focus_group.get_focused,
|
||||
LVGL::ANIM::OFF
|
||||
)
|
||||
end
|
||||
|
||||
@focus_group.each do |el|
|
||||
LVGUI.focus_group.add_obj(el)
|
||||
end
|
||||
end
|
||||
|
||||
# Switch to this window
|
||||
def present()
|
||||
LVGL::FFI.lv_disp_load_scr(@screen.lv_obj_pointer)
|
||||
reset_focus_group
|
||||
|
||||
# Allow the window to do some work every time it is switched to.
|
||||
on_present
|
||||
end
|
||||
|
||||
def on_present()
|
||||
end
|
||||
end
|
||||
end
|
301
boot/lib/lvgui/lvgui/gui.rb
Normal file
301
boot/lib/lvgui/lvgui/gui.rb
Normal file
@ -0,0 +1,301 @@
|
||||
module LVGUI
|
||||
# Refreshing at 120 times per second *really* helps with the drag operations
|
||||
# responsiveness. At 60 it feels a bit sluggish.
|
||||
# This likely comes from the naïve implementation that we are not refreshing at
|
||||
# 60 times per seconds, but rather, refresh and wait 1/60th of a second. This
|
||||
# makes the refresh rate a tad slower.
|
||||
# Boosting to 120 doesn't seem to have ill effects. It's simply refreshed more.
|
||||
REFRESH_RATE = 120
|
||||
|
||||
# UI constants
|
||||
NIXOS_LIGHT_HUE = 205
|
||||
NIXOS_DARK_HUE = 220
|
||||
|
||||
# Sets things up; back box for some ugly hacks.
|
||||
def self.init()
|
||||
return if @initialized
|
||||
@initialized = true
|
||||
|
||||
# This is used by the "simulator".
|
||||
if Args.get(:resolution)
|
||||
pair = Args.get(:resolution).split("x")
|
||||
unless pair.length == 2
|
||||
$stderr.puts "--resolution <width>x<height>"
|
||||
exit 2
|
||||
end
|
||||
LVGL::Hacks.monitor_width = pair.first.to_i
|
||||
LVGL::Hacks.monitor_height = pair.last.to_i
|
||||
else
|
||||
LVGL::Hacks.monitor_width = 720
|
||||
LVGL::Hacks.monitor_height = 1280
|
||||
end
|
||||
|
||||
# Get exclusive control of the framebuffer
|
||||
# By design we will not restore the console at exit.
|
||||
# We are assuming the target does not necessarily have a console attached to
|
||||
# the framebuffer, so this program has to be enough by itself.
|
||||
VTConsole.map_console(0)
|
||||
|
||||
puts "init"
|
||||
# XXX : here is the hang
|
||||
# Prepare LVGL
|
||||
LVGL::Hacks.init()
|
||||
puts "derp"
|
||||
|
||||
# Start the animation core
|
||||
LVGL::FFI.lv_anim_core_init()
|
||||
|
||||
# And switch to the desired theme
|
||||
LVGL::Hacks.theme_night(NIXOS_LIGHT_HUE)
|
||||
end
|
||||
|
||||
# Runs the app, black boxes LVGL things.
|
||||
def self.main_loop()
|
||||
# Main loop
|
||||
while true
|
||||
LVGL::Hacks::LVTask.handle_tasks
|
||||
sleep(1.0/REFRESH_RATE)
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def self.focus_group()
|
||||
LVGL::LVGroup.from_pointer(
|
||||
LVGL::FFI.lvgui_get_focus_group
|
||||
)
|
||||
end
|
||||
|
||||
def self.focus_ring_disable()
|
||||
LVGL::FFI.lvgui_focus_ring_disable()
|
||||
end
|
||||
|
||||
# Wraps an LVGL widget.
|
||||
class Widget
|
||||
def initialize(widget)
|
||||
@widget = widget
|
||||
end
|
||||
def method_missing(*args)
|
||||
@widget.send(*args)
|
||||
end
|
||||
|
||||
# Needed to make respond_to? work.
|
||||
def lv_obj_pointer()
|
||||
@widget.lv_obj_pointer
|
||||
end
|
||||
end
|
||||
|
||||
class Button < Widget
|
||||
def initialize(parent)
|
||||
super(LVGL::LVButton.new(parent))
|
||||
set_layout(LVGL::LAYOUT::COL_M)
|
||||
set_ink_in_time(200)
|
||||
set_ink_wait_time(100)
|
||||
set_ink_out_time(500)
|
||||
set_fit2(LVGL::FIT::FILL, LVGL::FIT::TIGHT)
|
||||
@label = LVGL::LVLabel.new(self)
|
||||
end
|
||||
|
||||
def set_label(label)
|
||||
@label.set_text(label)
|
||||
end
|
||||
end
|
||||
|
||||
# Common pattern for a "back button".
|
||||
# Handles its presentation, and handles its behaviour.
|
||||
class BackButton < Button
|
||||
# +parent+: Parent object
|
||||
# +location+: An instance on which `present` can be called.
|
||||
def initialize(parent, location)
|
||||
@holder = LVGL::LVContainer.new(parent)
|
||||
@holder.set_fit2(LVGL::FIT::FILL, LVGL::FIT::TIGHT)
|
||||
@holder.set_style(LVGL::CONT_STYLE::MAIN, LVGL::LVStyle::STYLE_TRANSP.dup)
|
||||
style = @holder.get_style(LVGL::CONT_STYLE::MAIN)
|
||||
style.body_padding_top = 0
|
||||
style.body_padding_left = 0
|
||||
style.body_padding_right = 0
|
||||
style.body_padding_bottom = 0
|
||||
|
||||
super(@holder)
|
||||
|
||||
@location = location
|
||||
set_label("#{LVGL::Symbols::LEFT} Back")
|
||||
set_fit2(LVGL::FIT::NONE, LVGL::FIT::TIGHT)
|
||||
set_width(@holder.get_width / 2)
|
||||
set_x(0)
|
||||
|
||||
self.event_handler = ->(event) do
|
||||
case event
|
||||
when LVGL::EVENT::CLICKED
|
||||
location.present()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Implements a clock as a wrapped LVLabel.
|
||||
class Clock < Widget
|
||||
def initialize(parent)
|
||||
super(LVGL::LVLabel.new(parent))
|
||||
set_align(LVGL::LABEL_ALIGN::LEFT)
|
||||
set_long_mode(LVGL::LABEL_LONG::CROP)
|
||||
|
||||
# Update the text once
|
||||
update_clock
|
||||
|
||||
# Then register a task to update regularly.
|
||||
@task = LVGL::Hacks::LVTask.create_task(250, LVGL::TASK_PRIO::MID, ->() do
|
||||
update_clock
|
||||
end)
|
||||
end
|
||||
|
||||
def update_clock()
|
||||
now = Time.now
|
||||
set_text([
|
||||
:hour,
|
||||
:min,
|
||||
:sec,
|
||||
].map{|fn| now.send(fn).to_s.rjust(2, "0") }.join(":"))
|
||||
end
|
||||
end
|
||||
|
||||
# Implements a battery widget as a wrapped LVLabel.
|
||||
class Battery < Widget
|
||||
def initialize(parent)
|
||||
super(LVGL::LVLabel.new(parent))
|
||||
set_align(LVGL::LABEL_ALIGN::RIGHT)
|
||||
set_long_mode(LVGL::LABEL_LONG::CROP)
|
||||
|
||||
@battery = HAL::Battery.main_battery
|
||||
|
||||
# Update the text once
|
||||
update_text
|
||||
|
||||
# Then register a task to update regularly.
|
||||
@task = LVGL::Hacks::LVTask.create_task(1000 * 15, LVGL::TASK_PRIO::LOW, ->() do
|
||||
update_text
|
||||
end)
|
||||
end
|
||||
|
||||
def update_text()
|
||||
if @battery
|
||||
symbol =
|
||||
if @battery.charging? then
|
||||
LVGL::Symbols::CHARGE
|
||||
elsif @battery.percent == "unknown"
|
||||
""
|
||||
elsif @battery.percent > 95
|
||||
LVGL::Symbols::BATTERY_FULL
|
||||
elsif @battery.percent > 75
|
||||
LVGL::Symbols::BATTERY_3
|
||||
elsif @battery.percent > 45
|
||||
LVGL::Symbols::BATTERY_2
|
||||
elsif @battery.percent > 10
|
||||
LVGL::Symbols::BATTERY_1
|
||||
else
|
||||
LVGL::Symbols::BATTERY_EMPTY
|
||||
end
|
||||
|
||||
set_text("#{symbol} #{@battery.percent}%")
|
||||
else
|
||||
set_text("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Empty invisible widget
|
||||
class Screen < Widget
|
||||
def initialize()
|
||||
super(LVGL::LVContainer.new())
|
||||
set_layout(LVGL::LAYOUT::COL_M)
|
||||
|
||||
style = get_style(LVGL::CONT_STYLE::MAIN).dup
|
||||
set_style(LVGL::CONT_STYLE::MAIN, style)
|
||||
style.body_padding_top = 0
|
||||
style.body_padding_left = 0
|
||||
style.body_padding_right = 0
|
||||
style.body_padding_bottom = 0
|
||||
style.body_padding_inner = 0
|
||||
end
|
||||
end
|
||||
|
||||
# Scrolling page.
|
||||
class Page < Widget
|
||||
def initialize(parent)
|
||||
@parent = parent
|
||||
# A "holder" widget to work around idiosyncracies of pages.
|
||||
@holder = LVGL::LVContainer.new(parent)
|
||||
@holder.set_fit2(LVGL::FIT::FILL, LVGL::FIT::NONE)
|
||||
@holder.set_style(LVGL::CONT_STYLE::MAIN, LVGL::LVStyle::STYLE_TRANSP.dup)
|
||||
|
||||
# The actual widget we interact with
|
||||
super(LVGL::LVPage.new(@holder))
|
||||
style = LVGL::LVStyle::STYLE_TRANSP.dup
|
||||
# Padding to zero in the actual scrolling widget makes the scrollbar visible
|
||||
style.body_padding_left = 0
|
||||
style.body_padding_right = 0
|
||||
|
||||
set_style(LVGL::PAGE_STYLE::BG, style)
|
||||
set_style(LVGL::PAGE_STYLE::SCRL, style)
|
||||
set_fit2(LVGL::FIT::FILL, LVGL::FIT::NONE)
|
||||
|
||||
# Make this scroll
|
||||
set_scrl_layout(LVGL::LAYOUT::COL_M)
|
||||
|
||||
refresh
|
||||
end
|
||||
|
||||
# Call this function when the position of the Page is changed.
|
||||
# Mainly, this would be after filling the toolbar.
|
||||
def refresh()
|
||||
# Filling the parent that is at the root of the screen is apparently broken :/.
|
||||
@holder.set_height(@parent.get_height_fit - @holder.get_y)
|
||||
set_height(@holder.get_height - get_y)
|
||||
end
|
||||
end
|
||||
|
||||
# A container, with a new name
|
||||
class Toolbar < Widget
|
||||
def initialize(parent)
|
||||
super(LVGL::LVContainer.new(parent))
|
||||
set_height(0)
|
||||
set_fit2(LVGL::FIT::FILL, LVGL::FIT::TIGHT)
|
||||
|
||||
set_style(LVGL::CONT_STYLE::MAIN, LVGL::LVStyle::STYLE_TRANSP.dup)
|
||||
style = get_style(LVGL::CONT_STYLE::MAIN)
|
||||
style.body_padding_top = 0
|
||||
style.body_padding_bottom = 0
|
||||
end
|
||||
end
|
||||
|
||||
# Widget implementing the whole header
|
||||
class Header < Widget
|
||||
def initialize(parent)
|
||||
super(LVGL::LVContainer.new(parent))
|
||||
|
||||
header_style = get_style(LVGL::CONT_STYLE::MAIN).dup
|
||||
set_style(LVGL::CONT_STYLE::MAIN, header_style)
|
||||
header_style.glass = 1
|
||||
header_style.body_radius = 0
|
||||
header_style.body_opa = 255 * 0.6
|
||||
|
||||
set_fit2(LVGL::FIT::FILL, LVGL::FIT::TIGHT)
|
||||
set_layout(LVGL::LAYOUT::PRETTY)
|
||||
|
||||
# Split 50/50
|
||||
child_width = (
|
||||
get_width -
|
||||
header_style.body_padding_left -
|
||||
header_style.body_padding_right -
|
||||
header_style.body_padding_inner*2
|
||||
) / 2
|
||||
|
||||
# [00:00 ]
|
||||
@clock = Clock.new(self)
|
||||
@clock.set_width(child_width)
|
||||
|
||||
# [ 69%]
|
||||
@battery = Battery.new(self)
|
||||
@battery.set_width(child_width)
|
||||
end
|
||||
end
|
||||
end
|
92
boot/lib/lvgui/lvgui/hal.rb
Normal file
92
boot/lib/lvgui/lvgui/hal.rb
Normal file
@ -0,0 +1,92 @@
|
||||
# Some hardware abstraction, specific to a particular use-case,
|
||||
# but still must be generic enough.
|
||||
#
|
||||
# All of this must be implemented with the goal to produce a phone-oriented
|
||||
# GUI toolkit.
|
||||
module LVGUI::HAL
|
||||
# Provide battery information
|
||||
class Battery
|
||||
NODE_BASE = "/sys/class/power_supply"
|
||||
# Guesstimates the main battery for a list of likely candidates
|
||||
def self.main_battery()
|
||||
node = %w{
|
||||
battery
|
||||
bms
|
||||
BAT0
|
||||
*-battery
|
||||
}.map { |name| Dir.glob(File.join(NODE_BASE, name)).first }
|
||||
.compact
|
||||
.first
|
||||
|
||||
if node
|
||||
Battery.new(node)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(node)
|
||||
@node = node
|
||||
end
|
||||
|
||||
# google-walleye
|
||||
# [nixos@nixos:/sys/class/power_supply]$ cat battery/uevent
|
||||
# POWER_SUPPLY_NAME=battery
|
||||
# POWER_SUPPLY_INPUT_SUSPEND=0
|
||||
# POWER_SUPPLY_STATUS=Charging
|
||||
# POWER_SUPPLY_HEALTH=Good
|
||||
# POWER_SUPPLY_PRESENT=1
|
||||
# POWER_SUPPLY_CHARGE_TYPE=Fast
|
||||
# POWER_SUPPLY_CAPACITY=56
|
||||
# POWER_SUPPLY_SYSTEM_TEMP_LEVEL=0
|
||||
# POWER_SUPPLY_CHARGER_TEMP=364
|
||||
# POWER_SUPPLY_CHARGER_TEMP_MAX=803
|
||||
# POWER_SUPPLY_INPUT_CURRENT_LIMITED=1
|
||||
# POWER_SUPPLY_VOLTAGE_NOW=3780507
|
||||
# POWER_SUPPLY_VOLTAGE_MAX=4400000
|
||||
# POWER_SUPPLY_VOLTAGE_QNOVO=-22
|
||||
# POWER_SUPPLY_CURRENT_NOW=183105
|
||||
# POWER_SUPPLY_CURRENT_QNOVO=-22
|
||||
# POWER_SUPPLY_CONSTANT_CHARGE_CURRENT_MAX=2700000
|
||||
# POWER_SUPPLY_TEMP=320
|
||||
# POWER_SUPPLY_TECHNOLOGY=Li-ion
|
||||
# POWER_SUPPLY_STEP_CHARGING_ENABLED=0
|
||||
# POWER_SUPPLY_STEP_CHARGING_STEP=-1
|
||||
# POWER_SUPPLY_CHARGE_DISABLE=0
|
||||
# POWER_SUPPLY_CHARGE_DONE=0
|
||||
# POWER_SUPPLY_PARALLEL_DISABLE=0
|
||||
# POWER_SUPPLY_SET_SHIP_MODE=0
|
||||
# POWER_SUPPLY_CHARGE_FULL=2805000
|
||||
# POWER_SUPPLY_DIE_HEALTH=Cool
|
||||
# POWER_SUPPLY_RERUN_AICL=0
|
||||
# POWER_SUPPLY_DP_DM=0
|
||||
# POWER_SUPPLY_CHARGE_COUNTER=1455951
|
||||
# POWER_SUPPLY_CYCLE_COUNT=849
|
||||
def uevent()
|
||||
File.read(File.join(@node, "uevent")).split("\n").map do |line|
|
||||
key, value = line.split("=", 2)
|
||||
[key.downcase.to_sym, value]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
def name()
|
||||
uevent[:power_supply_name] || "unknown"
|
||||
end
|
||||
|
||||
def status()
|
||||
uevent[:power_supply_status] || "unknown"
|
||||
end
|
||||
|
||||
def percent()
|
||||
if uevent[:power_supply_capacity]
|
||||
uevent[:power_supply_capacity].to_i
|
||||
else
|
||||
"unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def charging?()
|
||||
status.downcase == "charging" || uevent[:power_supply_charge_done] == "1"
|
||||
end
|
||||
end
|
||||
end
|
53
boot/lib/lvgui/lvgui/windows.rb
Normal file
53
boot/lib/lvgui/lvgui/windows.rb
Normal file
@ -0,0 +1,53 @@
|
||||
# Helpers for window types
|
||||
|
||||
module LVGUI
|
||||
# Helper methods to help creating a "button palette" kind of window.
|
||||
module ButtonPalette
|
||||
def add_button(label)
|
||||
Button.new(@container).tap do |btn|
|
||||
add_to_focus_group(btn)
|
||||
btn.glue_obj(true)
|
||||
btn.set_label(label)
|
||||
btn.event_handler = ->(event) do
|
||||
case event
|
||||
when LVGL::EVENT::CLICKED
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_buttons(list)
|
||||
list.each do |pair|
|
||||
label, action = pair
|
||||
add_button(label, &action)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Window
|
||||
# Include with +include LVGUI::Window::WithBackButton+ and
|
||||
# use e.g. +goes_back_to ->() { MainWindow.instance }+
|
||||
module WithBackButton
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
# Class methods included by WithBackButton
|
||||
module ClassMethods
|
||||
# A lambda (or proc)'s return value will determine which instance
|
||||
# of an object the button will link to.
|
||||
#
|
||||
# This is done through a proc/lambda because otherwise it ends up
|
||||
# depending on the singleton instance of windows directly.
|
||||
def goes_back_to(prc)
|
||||
class_variable_get(:@@_after_initialize_callback) << proc do
|
||||
btn = LVGUI::BackButton.new(@toolbar, prc.call())
|
||||
add_to_focus_group(btn)
|
||||
@container.refresh
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
24
boot/lib/lvgui/vtconsole.rb
Normal file
24
boot/lib/lvgui/vtconsole.rb
Normal file
@ -0,0 +1,24 @@
|
||||
module VTConsole
|
||||
# Allows unmapping the "vtcon" console from the frame buffer
|
||||
# This will ensure it doesn't trample on our lvgui.
|
||||
def self.map_console(value)
|
||||
# BAD HACK!
|
||||
# This always tries to map/unmap the console from the framebuffer
|
||||
# even when not using the framebuffer output! (Simulator)
|
||||
# TODO: better introspection to allow the app to know it is running in a
|
||||
# simulated environment.
|
||||
begin
|
||||
# We don't know which one(s) are running on the frame buffer.
|
||||
Dir.glob("/sys/class/vtconsole/vtcon*").each do |dir|
|
||||
# So we check...
|
||||
if File.read(File.join(dir, "name")).strip.match(/frame buffer/)
|
||||
# And only write to those.
|
||||
File.open(File.join(dir, "bind"), "w") do |file|
|
||||
file.write(value.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user