package miaou-core

  1. Overview
  2. Docs
Miaou core/widgets (no drivers, no SDL)

Install

dune-project
 Dependency

Authors

Maintainers

Sources

v0.5.2.tar.gz
md5=60a3b9f181f24572a06a9492532bfdda
sha512=fcc35a275066be2900e6201782faf47503076fa4640f08cf78067835a6f447b74613009e55b2ac799adb7ca46f1bffa261fc5971753f2cc3c6bef327511c7ef6

doc/src/miaou_widgets_display/qr_code_widget.ml.html

Source file qr_code_widget.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
(*****************************************************************************)
(*                                                                           *)
(* SPDX-License-Identifier: MIT                                              *)
(* Copyright (c) 2025 Nomadic Labs <contact@nomadic-labs.com>                *)
(* Copyright (c) 2026 Mathias Bourgoin <mathias.bourgoin@atacama.tech>       *)
(*                                                                           *)
(*****************************************************************************)

module Helpers = Miaou_helpers.Helpers
module W = Widgets

type t = {data : string; matrix : bool array array; scale : int}

let create ~data ?(scale = 1) () =
  try
    match Qrc.encode data with
    | None -> Error "QR code generation failed: data too large"
    | Some qr ->
        let size = Qrc.Matrix.w qr in
        let matrix =
          Array.init size (fun y ->
              Array.init size (fun x -> Qrc.Matrix.get qr ~x ~y))
        in
        Ok {data; matrix; scale}
  with e ->
    Error
      (Printf.sprintf "QR code generation failed: %s" (Printexc.to_string e))

let update_data t ~data = create ~data ~scale:t.scale ()

let get_dimensions t =
  let size = Array.length t.matrix in
  (size * t.scale, size * t.scale)

let get_data t = t.data

let get_module t ~x ~y =
  let size = Array.length t.matrix in
  if x < 0 || x >= size || y < 0 || y >= size then
    invalid_arg
      (Printf.sprintf
         "get_module: coordinates (%d, %d) out of bounds (size=%d)"
         x
         y
         size)
  else t.matrix.(y).(x)

let render t ~focus:_ =
  let size = Array.length t.matrix in
  let scale = t.scale in

  (* Use half-block characters to make QR codes more square *)
  (* Terminal chars are ~2:1 (height:width), so we render 2 rows per character *)
  (* QR codes require Unicode blocks - ASCII fallback would break square aspect ratio *)
  let upper_half = "▀" in
  let lower_half = "▄" in
  let full_block = "█" in
  let empty = " " in

  (* Add quiet zone (2 modules minimum for small QR codes) *)
  let quiet_zone = 2 * scale in
  let total_width = (size + 4) * scale in
  let char_height = (size + (quiet_zone * 2) + 1) / 2 in
  (* 2 modules per char row *)

  let lines = ref [] in

  for char_y = 0 to char_height - 1 do
    let row = Buffer.create (total_width * 4) in
    (* estimate with escape codes *)
    for x = 0 to total_width - 1 do
      let module_y_upper = char_y * 2 in
      let module_y_lower = (char_y * 2) + 1 in

      (* Determine which zone we're in *)
      let get_module y x =
        if
          y < quiet_zone
          || y >= size + quiet_zone
          || x < quiet_zone
          || x >= size + quiet_zone
        then false (* quiet zone = white *)
        else t.matrix.(y - quiet_zone).(x - quiet_zone)
      in

      let upper_dark =
        if module_y_upper < size + (quiet_zone * 2) then
          get_module module_y_upper x
        else false
      in
      let lower_dark =
        if module_y_lower < size + (quiet_zone * 2) then
          get_module module_y_lower x
        else false
      in

      let char =
        match (upper_dark, lower_dark) with
        | false, false -> empty
        | true, true -> full_block
        | true, false -> upper_half
        | false, true -> lower_half
      in

      for _ = 1 to scale do
        Buffer.add_string row char
      done
    done ;
    lines := Buffer.contents row :: !lines
  done ;

  Helpers.concat_lines (List.rev !lines)

[@@@enforce_exempt] (* non-widget module *)