diff --git a/android/Sdk.zig b/android/Sdk.zig index c08dde0a..f95a5413 100644 --- a/android/Sdk.zig +++ b/android/Sdk.zig @@ -739,6 +739,10 @@ pub fn compileAppLibrary( exe.strip = (mode == .ReleaseSmall); exe.export_table = true; + // XXX: temporary! + // TODO: remove and fix __emutls_get_address problem instead + exe.single_threaded = true; + exe.defineCMacro("ANDROID", null); exe.linkLibC(); diff --git a/android/default_icon.png b/android/default_icon.png new file mode 100644 index 00000000..7ea4bd87 Binary files /dev/null and b/android/default_icon.png differ diff --git a/build_capy.zig b/build_capy.zig index bf8002cb..da623046 100644 --- a/build_capy.zig +++ b/build_capy.zig @@ -11,7 +11,8 @@ pub const CapyBuildOptions = struct { android: AndroidOptions = .{}, pub const AndroidOptions = struct { - version: AndroidSdk.AndroidVersion = .android5, + // As of 2022, 95% of Android devices use Android 8 (Oreo) or higher + version: AndroidSdk.AndroidVersion = .android8, }; }; @@ -113,7 +114,7 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void // This is a set of resources. It should at least contain a "mipmap/icon.png" resource that // will provide the application icon. .resources = &[_]AndroidSdk.Resource{ - .{ .path = "mipmap/icon.png", .content = .{ .path = "example/icon.png" } }, + .{ .path = "mipmap/icon.png", .content = .{ .path = "android/default_icon.png" } }, }, .aaudio = false, .opensl = false, @@ -128,7 +129,7 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void }; const app = sdk.createApp( - "app-template.apk", + "zig-out/capy-app.apk", step.root_src.?.getPath(step.builder), config, mode, @@ -147,6 +148,9 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void } // Make the app build when we invoke "zig build" or "zig build install" + // TODO: only invoke keystore if .build_config/android.keystore doesn't exist + // When doing take environment variables or prompt if they're unavailable + //step.step.dependOn(sdk.initKeystore(key_store, .{})); step.step.dependOn(app.final_step); //const b = step.builder; diff --git a/src/backend.zig b/src/backend.zig index af10fead..bbed7833 100644 --- a/src/backend.zig +++ b/src/backend.zig @@ -7,7 +7,13 @@ const backend = //if (@hasDecl(@import("root"), "capyBackend")) switch (builtin.os.tag) { .windows => @import("backends/win32/backend.zig"), .macos => @import("backends/macos/backend.zig"), - .linux, .freebsd => @import("backends/gtk/backend.zig"), + .linux, .freebsd => blk: { + if (builtin.target.isAndroid()) { + break :blk @import("backends/android/backend.zig"); + } else { + break :blk @import("backends/gtk/backend.zig"); + } + }, .freestanding => blk: { if (builtin.cpu.arch == .wasm32 or builtin.cpu.arch == .wasm64) { break :blk @import("backends/wasm/backend.zig"); diff --git a/src/backends/android/backend.zig b/src/backends/android/backend.zig new file mode 100644 index 00000000..d41dbbbb --- /dev/null +++ b/src/backends/android/backend.zig @@ -0,0 +1,111 @@ +const std = @import("std"); +const shared = @import("../shared.zig"); +const lib = @import("../../main.zig"); + +const EventFunctions = shared.EventFunctions(@This()); +const EventType = shared.BackendEventType; +const BackendError = shared.BackendError; +const MouseButton = shared.MouseButton; +//pub const PeerType = *c.GtkWidget; +pub const PeerType = *opaque {}; + +var activeWindows = std.atomic.Atomic(usize).init(0); +var hasInit: bool = false; + +pub fn init() BackendError!void { + if (!hasInit) { + hasInit = true; + } +} + +pub fn showNativeMessageDialog(msgType: shared.MessageType, comptime fmt: []const u8, args: anytype) void { + const msg = std.fmt.allocPrintZ(lib.internal.scratch_allocator, fmt, args) catch { + std.log.err("Could not launch message dialog, original text: " ++ fmt, args); + return; + }; + defer lib.internal.scratch_allocator.free(msg); + _ = msgType; + @panic("TODO: message dialogs on Android"); +} + +/// user data used for handling events +pub const EventUserData = struct { + user: EventFunctions = .{}, + class: EventFunctions = .{}, + userdata: usize = 0, + classUserdata: usize = 0, + peer: PeerType, + focusOnClick: bool = false, +}; + +pub inline fn getEventUserData(peer: PeerType) *EventUserData { + _ = peer; + //return @ptrCast(*EventUserData, @alignCast(@alignOf(EventUserData), c.g_object_get_data(@ptrCast(*c.GObject, peer), "eventUserData").?)); +} + +pub fn Events(comptime T: type) type { + _ = T; + return struct {}; +} + +pub const Window = struct { + source_dpi: u32 = 96, + scale: f32 = 1.0, + + pub usingnamespace Events(Window); + + pub fn create() BackendError!Window { + return Window{}; + } + + pub fn resize(self: *Window, width: c_int, height: c_int) void { + _ = self; + _ = width; + _ = height; + } + + pub fn setTitle(self: *Window, title: [*:0]const u8) void { + _ = self; + _ = title; + } + + pub fn setChild(self: *Window, peer: ?PeerType) void { + _ = self; + _ = peer; + } + + pub fn setSourceDpi(self: *Window, dpi: u32) void { + self.source_dpi = 96; + // TODO + const resolution = @as(f32, 96.0); + self.scale = resolution / @intToFloat(f32, dpi); + } + + pub fn show(self: *Window) void { + _ = self; + _ = activeWindows.fetchAdd(1, .Release); + } + + pub fn close(self: *Window) void { + _ = self; + @panic("TODO: close window"); + } +}; + +pub fn postEmptyEvent() void { + @panic("TODO: postEmptyEvent"); +} + +pub fn runStep(step: shared.EventLoopStep) bool { + _ = step; + return activeWindows.load(.Acquire) != 0; +} + +pub const backendExport = struct { + pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = msg; + + @breakpoint(); + unreachable; + } +}; diff --git a/src/backends/win32/win32.zig b/src/backends/win32/win32.zig index 4a751767..8e5310da 100644 --- a/src/backends/win32/win32.zig +++ b/src/backends/win32/win32.zig @@ -327,7 +327,7 @@ pub const SB_BOTH = 3; pub const SW_INVALIDATE = 0x0002; pub extern "comctl32" fn GetScrollInfo(hWnd: HWND, nBar: c_int, lpsi: *SCROLLINFO) callconv(WINAPI) BOOL; -pub extern "comctl32" fn SetScrollInfo(hWnd: HWND, nBar: c_int, lpsi: *const SCROLLINFO, redraw: BOOL) callconv(WINAPI) c_int; +pub extern "comctl32" fn SetScrollInfo(hWnd: HWND, nBar: c_int, lpsi: *const SCROLLINFO, redraw: BOOL) callconv(WINAPI) c_int; pub extern "comctl32" fn EnableScrollBar(hWnd: HWND, wSBflags: UINT, wArrows: UINT) callconv(WINAPI) BOOL; pub extern "comctl32" fn ScrollWindowEx(hWnd: HWND, dx: c_int, dy: c_int, prcScroll: ?*const RECT, prcClip: ?*const RECT, hrgnUpdate: ?HRGN, prcUpdate: ?LPRECT, flags: UINT) callconv(WINAPI) c_int; diff --git a/src/button.zig b/src/button.zig index 6234af12..61ecb099 100644 --- a/src/button.zig +++ b/src/button.zig @@ -36,7 +36,7 @@ pub const Button_Impl = struct { if (self.peer == null) { self.peer = try backend.Button.create(); self.peer.?.setEnabled(self.enabled.get()); - + self.peer.?.setLabel(self.label.get()); try self.show_events(); _ = try self.enabled.addChangeListener(.{ .function = wrapperEnabledChanged, .userdata = @ptrToInt(&self.peer) }); diff --git a/src/flat/button.zig b/src/flat/button.zig index 52e1cb9a..e80a67ce 100644 --- a/src/flat/button.zig +++ b/src/flat/button.zig @@ -28,8 +28,11 @@ pub const FlatButton = struct { const width = @intCast(u32, backend.getWidthFromPeer(events.peer)); const height = @intCast(u32, backend.getHeightFromPeer(events.peer)); - if (self.enabled) { ctx.setColor(0.8, 0.8, 0.8); } - else { ctx.setColor(0.7, 0.7, 0.7); } + if (self.enabled) { + ctx.setColor(0.8, 0.8, 0.8); + } else { + ctx.setColor(0.7, 0.7, 0.7); + } ctx.rectangle(0, 0, width, height); ctx.fill();