Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,20 @@ libdmview_release(claim);
```

The service draws the view and handles its input; the application talks to
it through the view's `SIGNAL` (a dmhaman handler it registered) and `env:`
variables. Claims stack: the newest one is shown, a release - or the
application ending without one, noticed through its pid - returns the
display to the previous claim and finally to the service's own view.
it through the claim: the view's properties (`.prop`), which the application
sets and reads by name - a change runs the view's `.change` handlers - and
the events the view emits (`EMIT`), which it takes in order:

```c
libdmview_prop_set_string(claim, "title", "Midnight Drive");
libdmview_event_t ev;
libdmview_wait_event(claim, &ev, 1000); /* { "select", 3 } */
```

See [docs/assembly.md](docs/assembly.md#the-channel-of-a-claim). Claims
stack: the newest one is shown, a release - or the application ending
without one, noticed through its pid - returns the display to the previous
claim and finally to the service's own view.

## libdmview

Expand Down
5 changes: 5 additions & 0 deletions apps/dmview/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ static void show_view(service_t* s, char* path)
uint16_t w = 0, h = 0;
(void)libdmview_get_size(s->view, &w, &h);
set_rotation(s, rotation_for(s, w, h));
(void)libdmview_attach(s->view, s->display); /* The channel of the claim it is shown for */
DMOD_LOG_INFO("dmview: showing %s on %s\n", path, s->name);
}
}
Expand Down Expand Up @@ -417,7 +418,11 @@ static void run(service_t* s)
if (next != NULL)
show_view(s, Dmod_StrDup(next));
if (libdmview_display_generation(s->display) != s->generation)
{
show_view(s, choose_view(s));
if (s->view != NULL)
(void)libdmview_attach(s->view, s->display); /* (the same view, another claim) */
}

libdmview_rect_t changed;
if (s->view != NULL && libdmview_render(s->view, &s->surface, &changed) > 0)
Expand Down
14 changes: 14 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,17 @@ view sees is its time 0 (`$time`, `.timer`).
A claim remembers the pid of the calling process - never a callback or a
pointer into it - so a process that ends without releasing leaves nothing
behind.

## The channel of a claim

The view's properties (`.prop`) and events (`EMIT`) - see
[assembly.md](assembly.md#the-channel-of-a-claim).

| Function | Description |
|----------|-------------|
| `int libdmview_prop_set_int(uint32_t claim, const char* name, int32_t value)` | Set an integer property; the view takes it in (and runs its `.change` handlers) on the service's thread. `-ENOENT`: no such claim or property, `-EINVAL`: not an integer |
| `int libdmview_prop_set_string(claim, name, const char* value)` | Set a string property, cut to its size |
| `int libdmview_prop_get_int(claim, name, int32_t* value)` | As the view or the application last set it |
| `int libdmview_prop_get_string(claim, name, char* value, size_t size)` | `-ERANGE`: cut to `size` |
| `int libdmview_wait_event(claim, libdmview_event_t* event, int32_t timeout_ms)` | The next event (`name`, `value`, `dropped` before it); 0: no wait, -1: no limit. `-ETIMEDOUT`, `-ENOENT` (released meanwhile) |
| `int libdmview_attach(libdmview_t view, libdmview_display_t display)` | For the service: connect the view it shows to the display's newest claim; `-ENOENT` without a claim |
51 changes: 51 additions & 0 deletions docs/assembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ variable - `RECT 0, 0, $width, 8, #3D85F5` takes its width from `$width`.
| `.entry label` | Start of the draw pass (required, once) |
| `.var $name, int, init [, env:NAME]` | 32-bit signed integer variable |
| `.var $name, str[N], "init" [, env:NAME]` | String variable holding up to N bytes |
| `.prop $name, int, init, "name"` | Integer **property**: a variable the application that claimed the display reads and sets by `name` - see [The channel of a claim](#the-channel-of-a-claim) (version 0.6) |
| `.prop $name, str[N], "init", "name"` | String property |
| `.change $var, label` | Handler run when the application changed the property `$var` (version 0.6) |
| `.font name, "spec"` | Font used by `TEXT`; `spec` is resolved by `libdmview` (e.g. `"sans-16"`) - see [Fonts](#fonts) |
| `.gradient name, LINEAR [, angle], stop, stop [, ...]` | Linear gradient - see [Gradients](#gradients) |
| `.gradient name, RADIAL [, cx, cy, rx, ry], stop, stop [, ...]` | Radial gradient - see [Gradients](#gradients) |
Expand Down Expand Up @@ -535,6 +538,7 @@ each other; one box has at most one handler per event.
| 0x84 | `SCROLLTO` | `@id, x, y` | Set the scroll offset of a scroll box (clamped to its content) |
| 0x85 | `RELOAD` | `str` | Load the image file `str` again (it changed on disk) and redraw where it is shown |
| 0x86 | `SETFOCUS` | `@id` | Move the focus to a focusable box |
| 0x87 | `EMIT` | `str [, n]` | Send the event `str` with the value `n` (default 0) to the application that claimed the display - see [The channel of a claim](#the-channel-of-a-claim) (version 0.6) |

Opcodes 0xF0-0xFF are reserved for extensions (e.g. `dmjs` script calls).

Expand Down Expand Up @@ -614,6 +618,53 @@ One mask then fits every theme and every state; an A4 mask takes half a
byte per pixel. A gradient is placed on the rectangle x/y/w/h. `IMAGE`
draws only images with colors - a mask shows nothing there.

## The channel of a claim

An application shows its view on a display by claiming it
(`libdmview_claim()`); the claim is also the channel between the two:

- **Properties** - `.prop` variables - are the view's side of a contract:
the application sets and reads them by their names
(`libdmview_prop_set_int()`, `_set_string()`, `_get_int()`,
`_get_string()`), and the view uses them like any variable. The names are
the view's own: two views with a property `"title"` never meet. They are
read from the view file when it is claimed, so a name or a type the view
does not have is an error at once (`-ENOENT`, `-EINVAL`).
- A change the application makes runs the view's **`.change`** handlers of
that property (after the variable has the new value) and redraws what
reads it. A change the view makes itself (`SET`, ...) is what the
application reads next, without a handler.
- **Events** - `EMIT "name", n` - go into the claim's queue in order; the
application takes them with `libdmview_wait_event()`. 16 are kept; when
the application is slower, the oldest go and the next event it takes
tells how many (`dropped`).

The service applies what the application set on its own thread, the only one
that touches the view - when it updates the view, within 100 ms. Properties
set before the view is shown are taken in when it is.

```
.prop $title, str[48], "-", "title" ; the application sets it
.prop $playing, int, 0, "playing"
.change $playing, show_state

play_clicked:
EMIT "toggle"
RET
row_clicked:
EMIT "select", $row
RET
```

```c
uint32_t claim;
libdmview_claim(NULL, "/app/player.dmv", &claim);
libdmview_prop_set_string(claim, "title", "Midnight Drive");
libdmview_event_t ev;
if (libdmview_wait_event(claim, &ev, 1000) == 0 && strcmp(ev.name, "select") == 0)
play(ev.value);
```

## dmenv change listener

dmenv itself needs an addition for `env:` variables (draft, to be added to
Expand Down
15 changes: 8 additions & 7 deletions docs/binary-format.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# dmview Binary Format (`.dmv`)

Status: **version 0.5** - follows the [assembly draft](assembly.md). Version
0.2 added gradients, 0.3 the `OPACITY` instruction, 0.4 `ICON`, 0.5 `APPEND`.
Status: **version 0.6** - follows the [assembly draft](assembly.md). Version
0.2 added gradients, 0.3 the `OPACITY` instruction, 0.4 `ICON`, 0.5 `APPEND`, 0.6
the channel of a claim (`.prop`, `.change`, `EMIT`).

A `.dmv` file is what `libtodmv` produces from `.dmvs` assembly and what
`libdmview` executes. Everything is **little-endian**; every table starts at a
Expand Down Expand Up @@ -34,7 +35,7 @@ reader finds them only through the header, never by position.
|--------|------|-------|---------|
| 0 | 4 | magic | `'D' 'M' 'V' 0` |
| 4 | 2 | version_major | 0 - a reader rejects any other |
| 6 | 2 | version_minor | 4 - a reader accepts this or lower |
| 6 | 2 | version_minor | 6 - a reader accepts this or lower (a writer puts the oldest version its view needs) |
| 8 | 4 | file_size | Size of the whole file |
| 12 | 2 | width | `.size` width, 0 = not given |
| 14 | 2 | height | `.size` height, 0 = not given |
Expand Down Expand Up @@ -100,10 +101,10 @@ variables, fonts, boxes, labels and dmenv bindings.
| Offset | Size | Field | Meaning |
|--------|------|-------|---------|
| 0 | 1 | type | 0 = integer, 1 = string |
| 1 | 1 | flags | bit 0: bound to dmenv (`env:`) |
| 1 | 1 | flags | bit 0: bound to dmenv (`env:`); bit 1: a property (`.prop`, version 0.6) - not both |
| 2 | 2 | capacity | Bytes of a string variable, 0 for an integer |
| 4 | 2 | name | String index of the name, without `$` |
| 6 | 2 | env | String index of the dmenv name, 0xFFFF without binding |
| 6 | 2 | env | String index of the dmenv name (bit 0) or of the property's name (bit 1), 0xFFFF without either |
| 8 | 4 | init | Initial integer, or string index of the initial string |

**Font** (4 bytes): name (string index), spec (string index, e.g. `sans-16`).
Expand All @@ -117,10 +118,10 @@ variables, fonts, boxes, labels and dmenv bindings.

| Offset | Size | Field | Meaning |
|--------|------|-------|---------|
| 0 | 1 | kind | 0 `.init`, 1 `.timer`, 2 `.key`, 3 `.navkeys` |
| 0 | 1 | kind | 0 `.init`, 1 `.timer`, 2 `.key`, 3 `.navkeys`, 4 `.change` (version 0.6) |
| 1 | 1 | arg | `.navkeys`: role (`NEXT` = 0 ... `OK` = 6) |
| 2 | 2 | label | Handler code word offset, 0xFFFF for `.navkeys` |
| 4 | 4 | value | `.timer`: milliseconds, `.key` / `.navkeys`: button index |
| 4 | 4 | value | `.timer`: milliseconds, `.key` / `.navkeys`: button index, `.change`: variable index |

**Gradient** (16 bytes)

Expand Down
12 changes: 8 additions & 4 deletions include/dmview_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/** Format version this header describes. */
#define DMV_VERSION_MAJOR 0
#define DMV_VERSION_MINOR 5
#define DMV_VERSION_MINOR 6

/** Header size: version 0.2 added the gradient tables at its end (0.3:
* OPACITY, 0.4: ICON, 0.5: APPEND - no change to the header). */
Expand Down Expand Up @@ -119,6 +119,7 @@ typedef enum
DMV_OP_SCROLLTO = 0x84,
DMV_OP_RELOAD = 0x85,
DMV_OP_SETFOCUS = 0x86,
DMV_OP_EMIT = 0x87, /**< Version 0.6 */
} dmv_opcode_t;

/** Instruction categories. */
Expand Down Expand Up @@ -279,6 +280,7 @@ typedef enum

/** Variable flags. */
#define DMV_VARF_ENV 0x01u /**< Bound to the dmenv variable `env` */
#define DMV_VARF_PROP 0x02u /**< A property of the view (.prop) - `env` is its name - version 0.6 */

/** View-level items (directives with a handler). */
typedef enum
Expand All @@ -287,6 +289,7 @@ typedef enum
DMV_ITEM_TIMER, /**< .timer value=ms, label */
DMV_ITEM_KEY, /**< .key value=button, label */
DMV_ITEM_NAVKEY, /**< .navkeys arg=role, value=button, no label */
DMV_ITEM_CHANGE, /**< .change value=variable index, label - version 0.6 */

DMV_ITEM_COUNT
} dmv_item_kind_t;
Expand Down Expand Up @@ -345,7 +348,7 @@ typedef struct
uint8_t flags; /**< DMV_VARF_* */
uint16_t capacity; /**< Bytes of a string variable, 0 for an integer */
uint16_t name; /**< String index of the name (without '$') */
uint16_t env; /**< String index of the dmenv name, or DMV_NONE */
uint16_t env; /**< String index of the dmenv name (DMV_VARF_ENV) or of the property name (DMV_VARF_PROP), or DMV_NONE */
int32_t init; /**< Initial integer, or string index of the initial string */
} dmv_var_t;

Expand Down Expand Up @@ -396,7 +399,7 @@ typedef struct
uint8_t kind; /**< dmv_item_kind_t */
uint8_t arg; /**< DMV_ITEM_NAVKEY: dmv_nav_role_t */
uint16_t label; /**< Handler code word offset, or DMV_NONE */
uint32_t value; /**< TIMER: ms, KEY / NAVKEY: button index */
uint32_t value; /**< TIMER: ms, KEY / NAVKEY: button index, CHANGE: variable index */
} dmv_item_t;

/** Symbol record (labels, for disassembly and debugging). */
Expand Down Expand Up @@ -566,7 +569,7 @@ typedef struct
#define F_SCR DMV_FLAGS_SCROLL
#define F_ALIGN DMV_FLAGS_ALIGN

#define DMV_OPCODE_TABLE_SIZE (DMV_OP_SETFOCUS + 1)
#define DMV_OPCODE_TABLE_SIZE (DMV_OP_EMIT + 1)

static const dmv_opcode_info_t dmv_opcode_table[DMV_OPCODE_TABLE_SIZE] = {
OP(DMV_OP_NOP, "NOP", FLOW, 0, 0, F_NONE, false, 0),
Expand Down Expand Up @@ -619,6 +622,7 @@ static const dmv_opcode_info_t dmv_opcode_table[DMV_OPCODE_TABLE_SIZE] = {
OP(DMV_OP_SCROLLTO, "SCROLLTO", ACTION, 3, 0, F_NONE, false, BOX, V16, V16),
OP(DMV_OP_RELOAD, "RELOAD", ACTION, 1, 0, F_NONE, false, STR),
OP(DMV_OP_SETFOCUS, "SETFOCUS", ACTION, 1, 0, F_NONE, false, BOX),
OP(DMV_OP_EMIT, "EMIT", ACTION, 2, 1, F_NONE, false, STR, V32),
};

#undef V16
Expand Down
42 changes: 42 additions & 0 deletions include/libdmview.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,46 @@ dmod_libdmview_api(1.0, int, _claim, ( const char* display, const char* view_pat
/** @brief Drop a claim. -ENOENT when there is none with this id. */
dmod_libdmview_api(1.0, int, _release, ( uint32_t claim ));

/* ---- The channel of a claim ----
*
* A claim is also a channel between the application and its view: the
* view's properties (`.prop $var, type, init, "name"`), which both may set
* and read - a change made by the application runs the view's `.change`
* handlers -, and the events the view emits (`EMIT "name", value`), which
* the application takes in order. Names are the view's own: two
* applications with a property "title" do not meet.
*
* The properties are read from the view file when it is claimed, so a
* name or a type the view does not have is an error at once. What the
* application sets reaches the view on the service's thread within
* 100 ms (the service's poll) - also before the view is shown.
*/

/** @brief Set an integer property of the claim's view. -ENOENT (no claim, no such property), -EINVAL (not an integer). */
dmod_libdmview_api(1.0, int, _prop_set_int, ( uint32_t claim, const char* name, int32_t value ));

/** @brief Set a string property (cut to its size). -ENOENT, -EINVAL (not a string). */
dmod_libdmview_api(1.0, int, _prop_set_string, ( uint32_t claim, const char* name, const char* value ));

/** @brief An integer property - as the view or the application last set it. -ENOENT, -EINVAL. */
dmod_libdmview_api(1.0, int, _prop_get_int, ( uint32_t claim, const char* name, int32_t* value ));

/** @brief A string property. -ENOENT, -EINVAL, -ERANGE (@p value too small: cut). */
dmod_libdmview_api(1.0, int, _prop_get_string, ( uint32_t claim, const char* name, char* value, size_t size ));

/**
* @brief Take the next event the claim's view emitted, waiting for one at
* most @p timeout_ms (0: does not wait, -1: no limit).
*
* @return 0, -ETIMEDOUT, -ENOENT (no such claim, or it was released while waiting)
*/
dmod_libdmview_api(1.0, int, _wait_event, ( uint32_t claim, libdmview_event_t* event, int32_t timeout_ms ));

/**
* @brief Connect a view to the display's newest claim (called by the
* service after it showed the view the claim is for): its properties
* and events go through the claim from now on. No claim: none.
*/
dmod_libdmview_api(1.0, int, _attach, ( libdmview_t view, libdmview_display_t display ));

#endif /* LIBDMVIEW_H */
14 changes: 14 additions & 0 deletions include/libdmview_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ typedef struct
uint16_t h;
} libdmview_rect_t;

/** Longest name of an event (EMIT) with its terminating zero. */
#define LIBDMVIEW_EVENT_NAME_MAX 32u

/** Events of a claim kept until the application takes them (the oldest are dropped). */
#define LIBDMVIEW_EVENT_QUEUE 16u

/** An event the view of a claim emitted (EMIT name, value). */
typedef struct
{
char name[LIBDMVIEW_EVENT_NAME_MAX];
int32_t value;
uint32_t dropped; /**< Events dropped before this one - the application was too slow */
} libdmview_event_t;

/** libdmview_update() result when nothing is pending. */
#define LIBDMVIEW_NO_DEADLINE 0xFFFFFFFFu

Expand Down
Loading
Loading