///|
pub fn abi_version() -> Int {
proton_abi_version_ffi()
}
///|
pub fn runtime_info_json() -> String raise NativeError {
require_native_text("runtime info JSON", PayloadBytesExcludingTerminator, (
buffer,
buffer_len,
required,
) => proton_runtime_info_json_ffi(buffer, buffer_len, required))
}
///|
fn decode_runtime_info(text : String) -> RuntimeInfo raise NativeError {
let json = @json.parse(text) catch {
error =>
raise InvalidPayload(
context="runtime info JSON",
message=error.to_string(),
)
}
let info : RuntimeInfo = @json.from_json(json) catch {
error =>
raise InvalidPayload(context="runtime info", message=error.to_string())
}
expect_abi_version(info.abi_version, "runtime info")
info
}
///|
pub fn runtime_info() -> RuntimeInfo raise NativeError {
decode_runtime_info(runtime_info_json())
}
///|
pub fn AppActivation::new(
urls? : Array[String] = [],
files? : Array[String] = [],
reopen? : Bool = false,
) -> AppActivation {
AppActivation::{
abi_version: 1,
urls: urls.copy(),
files: files.copy(),
reopen,
}
}
///|
/// Claims one operating-system application identity. A secondary process
/// forwards its activation before returning `Forwarded`.
pub fn AppInstance::acquire(
identifier : String,
activation : AppActivation,
) -> AppInstanceAcquire raise NativeError {
if identifier.trim().to_owned() == "" {
raise invalid_argument("app instance identifier must not be empty")
}
let out_instance = Ref(proton_invalid_handle)
let out_primary = Ref(0)
let status = proton_app_instance_acquire_ffi(
@ffi.to_cstr(identifier),
@ffi.to_cstr(activation.to_json().stringify()),
out_instance,
out_primary,
)
if status < 0 {
raise native_error(status)
} else if out_primary.val == 0 {
AppInstanceAcquire::Forwarded
} else if out_instance.val == proton_invalid_handle {
raise InvalidPayload(
context="app instance",
message="primary acquisition returned an invalid handle",
)
} else {
AppInstanceAcquire::Primary(AppInstance::{
handle: out_instance.val,
destroyed: false,
})
}
}
///|
/// Attaches the primary instance listener to a runtime's existing wake source.
pub fn AppInstance::attach_runtime(
self : AppInstance,
runtime : Runtime,
) -> Unit raise NativeError {
if self.destroyed {
raise invalid_argument("app instance is destroyed")
}
check_status(
proton_app_instance_attach_runtime_ffi(self.handle, runtime.active_handle()),
)
}
///|
pub fn AppInstance::destroy(self : AppInstance) -> Unit raise NativeError {
if self.destroyed {
return
}
check_status(proton_app_instance_destroy_ffi(self.handle))
self.handle = proton_invalid_handle
self.destroyed = true
}
///|
pub fn last_error_message() -> String {
let required = proton_last_error_message_ffi(FixedArray::make(1, b'\x00'), 0)
if required <= 0 {
return ""
}
let buffer = FixedArray::make(required + 1, b'\x00')
let _ = proton_last_error_message_ffi(buffer, buffer.length())
@ffi.from_cstr(Bytes::from_array(buffer))
}
///|
fn native_error(status : Int) -> NativeError {
Status(status~, message=last_error_message())
}
///|
fn check_status(status : Int) -> Unit raise NativeError {
if status < 0 {
raise native_error(status)
}
}
///|
fn invalid_argument(message : String) -> NativeError {
InvalidArgument(message~)
}
///|
fn validate_window_config(config : WindowConfig) -> Unit raise NativeError {
match config.raw_json {
Some(_) => ()
None =>
if config.width <= 0 || config.height <= 0 {
raise invalid_argument("width and height must be positive")
}
}
}
///|
fn decode_native_json(text : String, label : String) -> Json raise NativeError {
@json.parse(text) catch {
error =>
raise InvalidPayload(
context=label + " JSON",
message="invalid JSON: " + error.to_string(),
)
}
}
///|
fn decode_string_json(json : Json, label : String) -> String raise NativeError {
@json.from_json(json) catch {
error =>
raise InvalidPayload(
context=label,
message="expected string: " + error.to_string(),
)
}
}
///|
fn decode_int_json(json : Json, label : String) -> Int raise NativeError {
@json.from_json(json) catch {
error =>
raise InvalidPayload(
context=label,
message="expected integer: " + error.to_string(),
)
}
}
///|
fn decode_bool_json(json : Json, label : String) -> Bool raise NativeError {
@json.from_json(json) catch {
error =>
raise InvalidPayload(
context=label,
message="expected boolean: " + error.to_string(),
)
}
}
///|
/// Rejects payloads whose `abi_version` does not match the schema version
/// this binding implements.
fn expect_abi_version(found : Int, context : String) -> Unit raise NativeError {
if found != 1 {
raise InvalidPayload(
context~,
message="unsupported abi_version " + found.to_string() + ", expected 1",
)
}
}
///|
/// Validates an optional `abi_version` field: absent is accepted (older
/// runtimes do not emit one), but a present field must be the number 1.
fn decode_optional_abi_version(
object : Map[String, Json],
context : String,
) -> Unit raise NativeError {
match object.get("abi_version") {
None => ()
Some(Number(version, ..)) => expect_abi_version(version.to_int(), context)
Some(_) =>
raise InvalidPayload(context~, message="expected number abi_version")
}
}
///|
fn decode_runtime_event_json(text : String) -> RuntimeEvent raise NativeError {
let json = decode_native_json(text, "runtime event")
let object = match json {
Object(object) => object
_ =>
raise InvalidPayload(context="runtime event", message="expected object")
}
decode_optional_abi_version(object, "runtime event")
let event_type_json = match object.get("type") {
Some(value) => value
None =>
raise InvalidPayload(context="runtime event", message="missing type")
}
let event_type = decode_string_json(event_type_json, "runtime event type")
let window = match object.get("window") {
None => None
Some(value) => Some(decode_int64_json(value, "runtime event window"))
}
let state = match object.get("state") {
None => None
Some(value) =>
Some(
@json.from_json(value) catch {
error =>
raise InvalidPayload(
context="runtime event window state",
message=error.to_string(),
)
},
)
}
let request_id = match object.get("request_id") {
None => None
Some(value) => Some(decode_int64_json(value, "runtime event request id"))
}
let url = match object.get("url") {
None => None
Some(value) => Some(decode_string_json(value, "runtime event URL"))
}
let http_method = match object.get("method") {
None => None
Some(value) => Some(decode_string_json(value, "runtime event method"))
}
let user_gesture = match object.get("user_gesture") {
None => None
Some(value) => Some(decode_bool_json(value, "runtime event user gesture"))
}
let redirect = match object.get("redirect") {
None => None
Some(value) => Some(decode_bool_json(value, "runtime event redirect"))
}
let disposition = match object.get("disposition") {
None => None
Some(value) => Some(decode_int_json(value, "runtime event disposition"))
}
let download_id = match object.get("download_id") {
None => None
Some(value) => Some(decode_int_json(value, "runtime event download id"))
}
let suggested_name = match object.get("suggested_name") {
None => None
Some(value) =>
Some(decode_string_json(value, "runtime event suggested name"))
}
let download_state = match object.get("download_state") {
None => None
Some(value) =>
Some(decode_string_json(value, "runtime event download state"))
}
let received_bytes = match object.get("received_bytes") {
None => None
Some(value) =>
Some(decode_int64_json(value, "runtime event received bytes"))
}
let total_bytes = match object.get("total_bytes") {
None => None
Some(value) => Some(decode_int64_json(value, "runtime event total bytes"))
}
let percent = match object.get("percent") {
None => None
Some(value) => Some(decode_int_json(value, "runtime event percent"))
}
let error_code = match object.get("error") {
None => None
Some(value) => Some(decode_int_json(value, "runtime event error code"))
}
let permissions = match object.get("permissions") {
None => None
Some(value) => Some(decode_int_json(value, "runtime event permissions"))
}
let menu_command_id = match object.get("command_id") {
None => None
Some(value) => Some(decode_string_json(value, "runtime event command id"))
}
let revision = match object.get("revision") {
None => None
Some(value) => Some(decode_int64_json(value, "runtime event revision"))
}
let items = match object.get("items") {
None => []
Some(Array(values)) => {
let items : Array[String] = []
for value in values {
items.push(decode_string_json(value, "runtime event item"))
}
items
}
Some(_) =>
raise InvalidPayload(
context="runtime event items",
message="expected string array",
)
}
let ok = match object.get("ok") {
None => None
Some(True) => Some(true)
Some(False) => Some(false)
Some(_) =>
raise InvalidPayload(
context="runtime event ok",
message="expected boolean",
)
}
let message = match object.get("message") {
None => None
Some(value) => Some(decode_string_json(value, "runtime event message"))
}
RuntimeEvent::{
event_type,
window,
state,
request_id,
url,
http_method,
user_gesture,
redirect,
disposition,
download_id,
suggested_name,
download_state,
received_bytes,
total_bytes,
percent,
error_code,
permissions,
menu_command_id,
revision,
items,
ok,
message,
}
}
///|
fn decode_int64_json(json : Json, label : String) -> Int64 raise NativeError {
@json.from_json(json) catch {
error =>
raise InvalidPayload(
context=label,
message="expected int64: " + error.to_string(),
)
}
}
///|
fn decode_bridge_request_json(text : String) -> BridgeRequest raise NativeError {
let json = decode_native_json(text, "bridge request")
let object = match json {
Object(object) => object
_ =>
raise InvalidPayload(context="bridge request", message="expected object")
}
decode_optional_abi_version(object, "bridge request")
let request_id_json = match object.get("request_id") {
Some(value) => value
None =>
raise InvalidPayload(
context="bridge request",
message="missing request_id",
)
}
let window_json = match object.get("window") {
Some(value) => value
None =>
raise InvalidPayload(context="bridge request", message="missing window")
}
let op_json = match object.get("op") {
Some(value) => value
None => raise InvalidPayload(context="bridge request", message="missing op")
}
let payload = object.get("payload").unwrap_or(Json::empty_object())
let page_instance = match object.get("page_instance") {
Some(String(value)) => Some(value)
_ => None
}
let source_origin = match object.get("source_origin") {
Some(value) => decode_string_json(value, "bridge request source_origin")
None =>
raise InvalidPayload(
context="bridge request",
message="missing source_origin",
)
}
let request_id = decode_int64_json(request_id_json, "bridge request_id")
let window = decode_int64_json(window_json, "bridge request window")
let op = decode_string_json(op_json, "bridge request op")
BridgeRequest::{
request_id,
window,
op,
payload,
page_instance,
source_origin,
}
}
///|
pub fn execute_process(
config? : RuntimeConfig = RuntimeConfig::new(),
) -> ProcessResult raise NativeError {
let out_exit_code = Ref(0)
let status = proton_execute_process_ffi(
@ffi.to_cstr(config.to_json_string()),
out_exit_code,
)
if status < 0 {
raise native_error(status)
} else if status == proton_process_handled {
ProcessResult::SubprocessHandled(out_exit_code.val)
} else {
ProcessResult::MainProcess
}
}
///|
/// Runs `entry` on Proton's application thread while the caller owns the
/// platform UI loop on runtimes that advertise `managed_app_runner`. Other
/// platforms may execute `entry` inline.
pub fn run_app(entry : FuncRef[() -> Unit]) -> Unit raise NativeError {
check_status(proton_app_run_ffi(entry))
}
///|
pub fn probe_runtime(
config? : RuntimeConfig = RuntimeConfig::new(),
) -> Unit raise NativeError {
check_status(
proton_runtime_probe_json_ffi(@ffi.to_cstr(config.to_json_string())),
)
}
///|
pub fn RuntimeConfig::probe(self : RuntimeConfig) -> Unit raise NativeError {
probe_runtime(config=self)
}
///|
pub fn Runtime::new(
config? : RuntimeConfig = RuntimeConfig::new(),
) -> Runtime raise NativeError {
let out_runtime = Ref(proton_invalid_handle)
let status = proton_runtime_create_json_ffi(
@ffi.to_cstr(config.to_json_string()),
out_runtime,
)
if status < 0 {
raise native_error(status)
} else {
Runtime::{ handle: out_runtime.val, lifecycle: RuntimeActive }
}
}
///|
pub fn Runtime::destroy(self : Runtime) -> Unit raise NativeError {
match self.lifecycle {
RuntimeDestroyed => return
RuntimeActive | RuntimeDestroying => self.lifecycle = RuntimeDestroying
}
let status = proton_runtime_destroy_ffi(self.handle)
if status < 0 {
raise native_error(status)
} else {
self.handle = proton_invalid_handle
self.lifecycle = RuntimeDestroyed
}
}
///|
fn Runtime::active_handle(self : Runtime) -> Int64 raise NativeError {
match self.lifecycle {
RuntimeActive => self.handle
RuntimeDestroying | RuntimeDestroyed =>
raise Status(
status=proton_err_invalid_state,
message="runtime is destroying or destroyed",
)
}
}
///|
/// Runs CEF's message loop for a low-level host.
///
/// Managed application runners own this loop and reject this operation.
pub fn Runtime::run(self : Runtime) -> Unit raise NativeError {
check_status(proton_runtime_run_ffi(self.active_handle()))
}
///|
/// Advances one external-message-pump iteration for a low-level host.
///
/// Managed application runners own the native message loop and reject this
/// operation.
pub fn Runtime::do_message_loop_work(self : Runtime) -> Unit raise NativeError {
check_status(proton_runtime_do_message_loop_work_ffi(self.active_handle()))
}
///|
/// Waits for low-level external-pump work.
///
/// Managed application runners use `set_wakeup_fd` instead and reject this
/// operation.
pub fn Runtime::wait(
self : Runtime,
interest_mask~ : Int,
timeout_ms~ : Int,
) -> RuntimeWaitReady raise NativeError {
if interest_mask == runtime_wait_none {
raise invalid_argument("interest_mask is required")
}
if interest_mask.land(runtime_wait_all.lnot()) != 0 {
raise invalid_argument("interest_mask contains unsupported bits")
}
if timeout_ms < 0 {
raise invalid_argument("timeout_ms must be non-negative")
}
let ready_mask = Ref(runtime_wait_none)
let status = proton_runtime_wait_ffi(
self.active_handle(),
interest_mask,
timeout_ms,
ready_mask,
)
if status < 0 {
raise native_error(status)
} else {
RuntimeWaitReady::{ mask: ready_mask.val }
}
}
///|
/// Installs the write end of a non-blocking pipe used to wake the host async
/// runtime. The native runtime duplicates the descriptor and does not borrow
/// the caller's ownership.
pub fn Runtime::set_wakeup_fd(
self : Runtime,
wakeup_fd : Int,
) -> Unit raise NativeError {
if wakeup_fd < -1 {
raise invalid_argument("wakeup_fd must be -1 or a valid descriptor")
}
check_status(
proton_runtime_set_wakeup_fd_ffi(self.active_handle(), wakeup_fd),
)
}
///|
/// Prepares a platform-owned wakeup source and returns its locator.
///
/// Call `activate_wakeup_source` after opening the locator for reading.
pub fn Runtime::prepare_wakeup_source(
self : Runtime,
) -> String raise NativeError {
let handle = self.active_handle()
require_native_text(
"runtime wakeup source",
PayloadBytesExcludingTerminator,
(buffer, buffer_len, required) => {
proton_runtime_prepare_wakeup_source_ffi(
handle, buffer, buffer_len, required,
)
},
)
}
///|
/// Activates a prepared wakeup source after its reader is connected.
pub fn Runtime::activate_wakeup_source(
self : Runtime,
) -> Unit raise NativeError {
check_status(proton_runtime_activate_wakeup_source_ffi(self.active_handle()))
}
///|
/// Returns the delay until CEF next needs its external message pump serviced,
/// or `None` when no delayed pump is scheduled.
///
/// Managed application runners own the native message loop and reject this
/// operation.
pub fn Runtime::next_wakeup_delay_ms(self : Runtime) -> Int? raise NativeError {
let delay = Ref(-1L)
let status = proton_runtime_next_wakeup_delay_ms_ffi(
self.active_handle(),
delay,
)
if status < 0 {
raise native_error(status)
} else if delay.val < 0L {
None
} else if delay.val > 2147483647L {
Some(2147483647)
} else {
Some(delay.val.to_int())
}
}
///|
/// Replaces the application-level native menu bar for this runtime.
pub fn Runtime::set_menu(
self : Runtime,
menu : MenuBar,
) -> Unit raise NativeError {
check_status(
proton_runtime_set_menu_json_ffi(
self.active_handle(),
@ffi.to_cstr(menu.to_json_string()),
),
)
}
///|
/// Requests that a low-level host's CEF message loop stop.
///
/// Managed application runners own the native message loop and reject this
/// operation. Destroy the runtime to finish a managed application.
pub fn Runtime::quit(self : Runtime) -> Unit raise NativeError {
check_status(proton_runtime_quit_ffi(self.active_handle()))
}
///|
pub fn Runtime::poll_event_json(self : Runtime) -> String? raise NativeError {
let handle = self.active_handle()
read_native_text("runtime event JSON", PayloadBytesExcludingTerminator, (
buffer,
buffer_len,
required,
) => proton_runtime_poll_event_json_ffi(handle, buffer, buffer_len, required))
}
///|
pub fn Runtime::poll_event(self : Runtime) -> RuntimeEvent? raise NativeError {
match self.poll_event_json() {
Some(text) => Some(decode_runtime_event_json(text))
None => None
}
}
///|
pub fn Runtime::poll_bridge_request_json(
self : Runtime,
) -> String? raise NativeError {
let handle = self.active_handle()
read_native_text("bridge request JSON", PayloadBytesExcludingTerminator, (
buffer,
buffer_len,
required,
) => {
proton_runtime_poll_bridge_request_json_ffi(
handle, buffer, buffer_len, required,
)
})
}
///|
pub fn Runtime::poll_bridge_request(
self : Runtime,
) -> BridgeRequest? raise NativeError {
match self.poll_bridge_request_json() {
Some(text) => Some(decode_bridge_request_json(text))
None => None
}
}
///|
pub fn Runtime::respond_bridge_request(
self : Runtime,
response : BridgeResponse,
) -> Unit raise NativeError {
check_status(
proton_runtime_respond_bridge_request_json_ffi(
self.active_handle(),
@ffi.to_cstr(response.to_json_string()),
),
)
}
///|
pub fn Window::new(
runtime : Runtime,
config? : WindowConfig = WindowConfig::new(),
) -> Window raise NativeError {
validate_window_config(config)
let out_window = Ref(proton_invalid_handle)
let status = proton_window_create_json_ffi(
runtime.active_handle(),
@ffi.to_cstr(config.to_json_string()),
out_window,
)
if status < 0 {
raise native_error(status)
} else {
Window::{ handle: out_window.val, lifecycle: WindowLive }
}
}
///|
pub fn Window::destroy(self : Window) -> Unit raise NativeError {
match self.lifecycle {
WindowDestroyed => return
WindowLive | WindowCloseRequested | WindowDestroying =>
self.lifecycle = WindowDestroying
}
let status = proton_window_destroy_ffi(self.handle)
if status < 0 {
raise native_error(status)
} else {
self.handle = proton_invalid_handle
self.lifecycle = WindowDestroyed
}
}
///|
fn Window::live_handle(self : Window) -> Int64 raise NativeError {
match self.lifecycle {
WindowLive => self.handle
WindowCloseRequested | WindowDestroying | WindowDestroyed =>
raise Status(
status=proton_err_invalid_state,
message="window is closing or destroyed",
)
}
}
///|
pub fn Window::show(self : Window) -> Unit raise NativeError {
check_status(proton_window_show_ffi(self.live_handle()))
}
///|
/// Borrows this owning window handle for APIs that must not destroy it.
pub fn Window::as_ref(self : Window) -> WindowRef {
WindowRef::unsafe_from_handle(
match self.lifecycle {
WindowLive => self.handle
WindowCloseRequested | WindowDestroying | WindowDestroyed =>
proton_invalid_handle
},
)
}
///|
/// Returns the stable native handle used to correlate runtime events.
pub fn Window::id(self : Window) -> Int64 {
match self.lifecycle {
WindowLive | WindowCloseRequested => self.handle
WindowDestroying | WindowDestroyed => proton_invalid_handle
}
}
///|
pub fn Window::hide(self : Window) -> Unit raise NativeError {
check_status(proton_window_hide_ffi(self.live_handle()))
}
///|
pub fn Window::close(self : Window) -> Unit raise NativeError {
match self.lifecycle {
WindowCloseRequested | WindowDestroying | WindowDestroyed => return
WindowLive => ()
}
let status = proton_window_close_ffi(self.handle)
if status < 0 {
raise native_error(status)
} else {
self.lifecycle = WindowCloseRequested
}
}
///|
pub fn Window::focus(self : Window) -> Unit raise NativeError {
check_status(proton_window_focus_ffi(self.live_handle()))
}
///|
pub fn Window::set_title(
self : Window,
title : String,
) -> Unit raise NativeError {
check_status(
proton_window_set_title_ffi(self.live_handle(), @ffi.to_cstr(title)),
)
}
///|
pub fn Window::set_size(
self : Window,
width : Int,
height : Int,
) -> Unit raise NativeError {
if width <= 0 || height <= 0 {
raise invalid_argument("width and height must be positive")
}
check_status(proton_window_set_size_ffi(self.live_handle(), width, height))
}
///|
pub fn Window::minimize(self : Window) -> Unit raise NativeError {
check_status(proton_window_minimize_ffi(self.live_handle()))
}
///|
pub fn Window::maximize(self : Window) -> Unit raise NativeError {
check_status(proton_window_maximize_ffi(self.live_handle()))
}
///|
pub fn Window::restore(self : Window) -> Unit raise NativeError {
check_status(proton_window_restore_ffi(self.live_handle()))
}
///|
pub fn Window::set_fullscreen(
self : Window,
fullscreen : Bool,
) -> Unit raise NativeError {
check_status(
proton_window_set_fullscreen_ffi(
self.live_handle(),
if fullscreen {
1
} else {
0
},
),
)
}
///|
pub fn Window::set_position(
self : Window,
x : Int,
y : Int,
) -> Unit raise NativeError {
check_status(proton_window_set_position_ffi(self.live_handle(), x, y))
}
///|
pub fn Window::set_always_on_top(
self : Window,
always_on_top : Bool,
) -> Unit raise NativeError {
check_status(
proton_window_set_always_on_top_ffi(
self.live_handle(),
if always_on_top {
1
} else {
0
},
),
)
}
///|
pub fn Window::set_zoom_percent(
self : Window,
zoom_percent : Int,
) -> Unit raise NativeError {
if zoom_percent < 25 || zoom_percent > 500 {
raise invalid_argument("zoom_percent must be between 25 and 500")
}
check_status(
proton_window_set_zoom_percent_ffi(self.live_handle(), zoom_percent),
)
}
///|
pub fn Window::state(self : Window) -> WindowState raise NativeError {
let handle = self.live_handle()
let text = match
read_native_text("window state", PayloadBytesExcludingTerminator, (
buffer,
buffer_len,
required,
) => proton_window_state_json_ffi(handle, buffer, buffer_len, required)) {
Some(text) => text
None =>
raise InvalidPayload(context="window state", message="missing payload")
}
@json.from_json(decode_native_json(text, "window state")) catch {
error =>
raise InvalidPayload(context="window state", message=error.to_string())
}
}
///|
pub fn Window::set_close_interception(
self : Window,
enabled : Bool,
) -> Unit raise NativeError {
check_status(
proton_window_set_close_interception_ffi(
self.live_handle(),
if enabled {
1
} else {
0
},
),
)
}
///|
pub fn Window::respond_close_request(
self : Window,
request_id : Int64,
allow : Bool,
) -> Unit raise NativeError {
check_status(
proton_window_respond_close_request_ffi(
self.live_handle(),
request_id,
if allow {
1
} else {
0
},
),
)
}
///|
pub fn Window::load_url(self : Window, url : String) -> Unit raise NativeError {
check_status(
proton_window_load_url_ffi(self.live_handle(), @ffi.to_cstr(url)),
)
}
///|
pub fn Window::load_html(
self : Window,
html : String,
base_url : String,
) -> Unit raise NativeError {
check_status(
proton_window_load_html_ffi(
self.live_handle(),
@ffi.to_cstr(html),
@ffi.to_cstr(base_url),
),
)
}
///|
pub fn Window::eval(self : Window, script : String) -> Unit raise NativeError {
check_status(proton_window_eval_ffi(self.live_handle(), @ffi.to_cstr(script)))
}
///|
pub fn Window::browser_command(
self : Window,
command : String,
download_id? : Int,
) -> Unit raise NativeError {
let fields : Map[String, Json] = { "command": Json::string(command) }
match download_id {
Some(download_id) =>
fields["download_id"] = Json::number(
download_id.to_double(),
repr=download_id.to_string(),
)
None => ()
}
check_status(
proton_window_browser_command_json_ffi(
self.live_handle(),
@ffi.to_cstr(Json::object(fields).stringify()),
),
)
}
///|
pub fn Window::respond_browser_request(
self : Window,
request_id : Int64,
action : String,
path? : String,
) -> Unit raise NativeError {
let fields : Map[String, Json] = {
"request_id": Json::string(request_id.to_string()),
"action": Json::string(action),
}
match path {
Some(path) => fields["path"] = Json::string(path)
None => ()
}
check_status(
proton_window_respond_browser_request_json_ffi(
self.live_handle(),
@ffi.to_cstr(Json::object(fields).stringify()),
),
)
}
///|
pub fn Window::emit_bridge_event_json(
self : Window,
event_json : String,
) -> Unit raise NativeError {
check_status(
proton_window_emit_bridge_event_json_ffi(
self.live_handle(),
@ffi.to_cstr(event_json),
),
)
}
///|
fn window_bridge_json(
window : Window,
context : String,
query : (Int64, FixedArray[Byte], Int, Ref[Int]) -> Int,
) -> String? raise NativeError {
let handle = window.live_handle()
read_native_text(context, PayloadBytesExcludingTerminator, (
buffer,
buffer_len,
required,
) => query(handle, buffer, buffer_len, required))
}
///|
fn decode_bridge_lifecycle_state(
text : String,
) -> BridgeLifecycleState raise NativeError {
let json = decode_native_json(text, "bridge lifecycle state")
let state : BridgeLifecycleState = @json.from_json(json) catch {
error =>
raise InvalidPayload(
context="bridge lifecycle state",
message=error.to_string(),
)
}
expect_abi_version(state.abi_version, "bridge lifecycle state")
state
}
///|
pub fn Window::bridge_lifecycle_state(
self : Window,
) -> BridgeLifecycleState raise NativeError {
let text = match
window_bridge_json(
self, "bridge lifecycle state", proton_window_bridge_state_json_ffi,
) {
Some(text) => text
None =>
raise InvalidPayload(
context="bridge lifecycle state",
message="missing payload",
)
}
decode_bridge_lifecycle_state(text)
}
///|
fn decode_bridge_diagnostic(
text : String,
) -> BridgeDiagnostic raise NativeError {
let json = decode_native_json(text, "bridge diagnostic")
let diagnostic : BridgeDiagnostic = @json.from_json(json) catch {
error =>
raise InvalidPayload(
context="bridge diagnostic",
message=error.to_string(),
)
}
expect_abi_version(diagnostic.abi_version, "bridge diagnostic")
diagnostic
}
///|
pub fn Window::take_bridge_failure(
self : Window,
) -> BridgeDiagnostic? raise NativeError {
match
window_bridge_json(
self, "bridge diagnostic", proton_window_take_bridge_failure_json_ffi,
) {
None => None
Some(text) => Some(decode_bridge_diagnostic(text))
}
}
///|
fn optional_dialog_text_buffer(value : String?) -> Bytes {
match value {
Some(value) => @ffi.to_cstr(value)
None => @ffi.to_cstr("")
}
}
///|
fn dialog_text_payload_len(buffer : Bytes) -> Int {
let len = buffer.length()
if len >= 1 {
len - 1
} else {
len
}
}
///|
pub fn WindowRef::begin_message_dialog(
self : WindowRef,
title : String?,
message : String,
level : DialogLevel,
) -> Int64 raise NativeError {
let title_buffer = optional_dialog_text_buffer(title)
let message_buffer = @ffi.to_cstr(message)
let dialog = Ref(proton_invalid_handle)
let status = proton_window_begin_message_dialog_ffi(
self.handle,
title_buffer,
dialog_text_payload_len(title_buffer),
message_buffer,
dialog_text_payload_len(message_buffer),
level,
dialog,
)
if status < 0 {
raise native_error(status)
} else {
dialog.val
}
}
///|
pub fn Runtime::begin_message_dialog(
self : Runtime,
title : String?,
message : String,
level : DialogLevel,
) -> Int64 raise NativeError {
let title_buffer = optional_dialog_text_buffer(title)
let message_buffer = @ffi.to_cstr(message)
let dialog = Ref(proton_invalid_handle)
let status = proton_runtime_begin_message_dialog_ffi(
self.active_handle(),
title_buffer,
dialog_text_payload_len(title_buffer),
message_buffer,
dialog_text_payload_len(message_buffer),
level,
dialog,
)
if status < 0 {
raise native_error(status)
} else {
dialog.val
}
}
///|
pub fn Runtime::poll_message_dialog(
self : Runtime,
dialog : Int64,
) -> Bool raise NativeError {
let handle = self.active_handle()
match
read_native_text("message dialog result", BufferBytesIncludingTerminator, (
buffer,
buffer_len,
required,
) => {
proton_runtime_poll_dialog_result_ffi(
handle, dialog, buffer, buffer_len, required,
)
}) {
None => false
Some(_) => true
}
}
///|
pub fn WindowRef::begin_confirm_dialog(
self : WindowRef,
title : String?,
message : String,
level : DialogLevel,
) -> Int64 raise NativeError {
let title_buffer = optional_dialog_text_buffer(title)
let message_buffer = @ffi.to_cstr(message)
let dialog = Ref(proton_invalid_handle)
let status = proton_window_begin_confirm_dialog_ffi(
self.handle,
title_buffer,
dialog_text_payload_len(title_buffer),
message_buffer,
dialog_text_payload_len(message_buffer),
level,
dialog,
)
if status < 0 {
raise native_error(status)
} else {
dialog.val
}
}
///|
fn WindowRef::begin_file_dialog(
window : WindowRef,
title : String?,
path : String?,
callback : (Int64, Bytes, Int, Bytes, Int, Ref[Int64]) -> Int,
) -> Int64 raise NativeError {
let title_buffer = optional_dialog_text_buffer(title)
let path_buffer = optional_dialog_text_buffer(path)
let dialog = Ref(proton_invalid_handle)
let status = callback(
window.handle,
title_buffer,
dialog_text_payload_len(title_buffer),
path_buffer,
dialog_text_payload_len(path_buffer),
dialog,
)
if status < 0 {
raise native_error(status)
} else {
dialog.val
}
}
///|
pub fn WindowRef::begin_open_file_dialog(
self : WindowRef,
title : String?,
path : String?,
) -> Int64 raise NativeError {
self.begin_file_dialog(title, path, proton_window_begin_open_file_dialog_ffi)
}
///|
pub fn WindowRef::begin_save_file_dialog(
self : WindowRef,
title : String?,
path : String?,
) -> Int64 raise NativeError {
self.begin_file_dialog(title, path, proton_window_begin_save_file_dialog_ffi)
}
///|
pub fn WindowRef::begin_choose_directory_dialog(
self : WindowRef,
title : String?,
path : String?,
) -> Int64 raise NativeError {
self.begin_file_dialog(
title, path, proton_window_begin_choose_directory_dialog_ffi,
)
}
///|
pub fn WindowRef::poll_dialog_result(
self : WindowRef,
dialog : Int64,
) -> DialogPollResult raise NativeError {
let handle = self.handle
match
read_native_text("window dialog result", BufferBytesIncludingTerminator, (
buffer,
buffer_len,
required,
) => {
proton_window_poll_dialog_result_ffi(
handle, dialog, buffer, buffer_len, required,
)
}) {
None => Pending
Some(result) => Ready(result)
}
}