├── cliptomania.nimble ├── examples └── basic.nim ├── README.md └── src └── cliptomania.nim /cliptomania.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Guevara-chan" 5 | description = ".NET-inspired lightweight clipboard library" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 0.18.0" -------------------------------------------------------------------------------- /examples/basic.nim: -------------------------------------------------------------------------------- 1 | # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # 2 | # Basic formats coverage for Cliptomania 3 | # Developed in 2019 by V.A. Guevara 4 | # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # 5 | import strformat, "../src/cliptomania" 6 | 7 | # Text data coverage. 8 | let text_data = "Hallo there." 9 | echo fmt"Adding '{text_data}' to clipboard..." 10 | clip.set_text text_data 11 | if clip.contains_text: echo fmt"Retrieved back: {clip.get_text}" 12 | 13 | # Droplist coverage. 14 | let drop_data = @[r"C:\a.txt", r"d:\b.exe"] 15 | echo fmt"---{'\n'}Adding '{drop_data}' to clipboard..." 16 | clip.set_file_drop_list drop_data 17 | if clip.contains_file_drop_list: echo fmt"Retrieved back: {clip.get_file_drop_list}" 18 | 19 | # Final clearance. 20 | clip.clear 21 | echo "---\nClipboard content erased." -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # •Sum• [![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/yglukhov/nimble-tag) 2 | __Cliptomania__ is a lightweight clip library, made to recreate [Clipboard](https://docs.microsoft.com/ru-ru/dotnet/api/system.windows.clipboard) API for [Nim](https://nim-lang.org/). 3 | Full compatibility is not prioritized at given moment, but desirable in future. 4 | **Installation:** run `nimble install cliptomania` in terminal. 5 | ❗ Currently only compiles on Windows systems ❗ 6 | 7 | # •Featuræ• 8 | * Multiple data formats dispatch. 9 | * Converter-based data I/O interface. 10 | * Enforced prefix (`clip.`) API isolation. 11 | 12 | # •Remark• 13 | Since __Cliptomania__ was developed mainly for internal usage, no documentation will likely be included before v0.3. 14 | Provided examples and reading original .NET docs is a way to understand how to use this lib until then. 15 | -------------------------------------------------------------------------------- /src/cliptomania.nim: -------------------------------------------------------------------------------- 1 | # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # 2 | # Cliptomania clipboard library v0.1 3 | # Developed in 2019 by V.A. Guevara 4 | # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # 5 | 6 | # [OS-dependent bindings] 7 | when defined(windows): 8 | type 9 | Point = object 10 | x, y: int32 11 | DropFiles = object 12 | pFiles: int32 13 | pt: Point 14 | fNC, fWide: int32 15 | # •Clipboard• 16 | proc open_clipboard(hwnd: int = 0): cint {.stdcall, dynlib: "user32", importc: "OpenClipboard", discardable.} 17 | proc close_clipboard(): cint {.stdcall, dynlib: "user32", importc: "CloseClipboard", discardable.} 18 | proc empty_clipboard(): cint {.stdcall, dynlib: "user32", importc: "EmptyClipboard", discardable.} 19 | proc get_clipboard_data(format: uint): pointer {.stdcall, dynlib: "user32", importc: "GetClipboardData".} 20 | proc set_clipboard_data(format:uint, mem:pointer): pointer {.stdcall, dynlib:"user32", importc:"SetClipboardData".} 21 | proc clipboard_format_available(format:uint): cint {.stdcall, dynlib:"user32",importc:"IsClipboardFormatAvailable".} 22 | # •Global memory• 23 | proc global_size(mem: pointer): cint {.stdcall, dynlib: "kernel32", importc: "GlobalSize".} 24 | proc global_lock(mem: pointer): pointer {.stdcall, dynlib: "kernel32", importc: "GlobalLock".} 25 | proc global_alloc(flags:uint,size:int):pointer {.stdcall, dynlib: "kernel32", importc: "GlobalAlloc".} 26 | proc global_unlock(mem: pointer): cint {.stdcall, dynlib: "kernel32", importc: "GlobalUnlock", discardable.} 27 | else: {.fatal: "FAULT:: only Windows OS is supported for now !".} 28 | 29 | #.{ [Classes] 30 | when not defined(clip): 31 | # --Service definitions: 32 | type 33 | clip* = object 34 | ClipFragment = tuple[format: clip_formats, data: seq[byte]] 35 | clip_formats = enum 36 | text = 1, bitmap, metafile_picture, symbolic_link, dif, tiff, oem_text, dib, palette, pen_data, riff, 37 | wave_audio, unicode_text, enhanced_metafile, file_drop, locale, dib_v5 38 | template formats*(_: type clip): auto = clip_formats 39 | template fragment*(_: type clip): auto = ClipFragment 40 | using 41 | Δ: type clip 42 | format: clip.formats 43 | 44 | # --Methods goes here: 45 | # •Aux converters & helpers• 46 | converter to_clip_fragment*(src: string): clip.fragment = 47 | var wide_text = newWideCString(src) 48 | var buffer = newSeq[byte](wide_text.len * 2 + 2) 49 | buffer[0].addr.copyMem wide_text[0].addr, buffer.len 50 | return (format: clip.formats.unicode_text, data: buffer) 51 | 52 | converter to_clip_fragment*(src: seq[string]): clip.fragment = 53 | var 54 | buffer = newSeq[int16](DropFiles.sizeOf shr 1) 55 | header = cast[ptr DropFiles](buffer[0].addr) 56 | header.fWide = 1 57 | header.pFiles = DropFiles.sizeOf.int32 58 | for entry in src: 59 | var utf16_path = cast[seq[int16]](entry.to_clip_fragment.data) 60 | utf16_path.setLen utf16_path.len shr 1 61 | for c in utf16_path: buffer &= c 62 | buffer &= 0.int16 63 | buffer.setLen buffer.len * 2 64 | return (format: clip.formats.file_drop, data: cast[seq[byte]](buffer)) 65 | 66 | proc `$`*(src: clip.fragment): string = 67 | var utf16 = src.data 68 | return $cast[WideCString](utf16[0].addr) 69 | 70 | converter to_drop_list*(src: clip.fragment): seq[string] = 71 | var feed = src.data 72 | result = newSeq[string](0) 73 | var utf16_feed = cast[seq[int16]](feed) 74 | utf16_feed.setLen feed.len 75 | if feed.len > 0: 76 | var 77 | header = cast[ptr DropFiles](feed[0].addr) 78 | accum: seq[int16] = @[] 79 | for idx, byte in feed[header.pFiles..^1]: 80 | let c = (if header.fWide == 0: byte.int16 else: utf16_feed[idx+header.pFiles shr 1]) 81 | if c != 0: accum &= c 82 | elif accum != @[]: 83 | accum.setLen accum.len * 2 84 | result.add($(format: clip.formats.text, data: cast[seq[byte]](accum))) 85 | accum = @[] 86 | 87 | converter to_byte_seq(src: clip.fragment): seq[byte] = 88 | src.data 89 | 90 | converter to_clip_format(src: clip.fragment): clip.formats = 91 | src.format 92 | 93 | # •Public methods• 94 | proc get_data_list*(Δ; formats: varargs[clip.formats]): seq[clip.fragment] = 95 | result = newSeq[clip.fragment](0) 96 | open_clipboard() 97 | for format in formats: 98 | let data = format.uint.get_clipboard_data 99 | let data_size = data.global_size 100 | if data_size > 0: 101 | let feed = data.global_lock 102 | var buffer = newSeq[byte](data_size) 103 | buffer[0].addr.copyMem feed, data_size 104 | data.global_unlock 105 | result.add (format, buffer) 106 | else: result.add (format, @[]) 107 | close_clipboard() 108 | 109 | proc set_data_list*(Δ; list: varargs[clip.fragment]) = 110 | open_clipboard() 111 | empty_clipboard() 112 | for entry in list: 113 | var (format, data) = entry 114 | let buffer = 66.global_alloc data.len 115 | let dest = buffer.global_lock 116 | dest.copyMem data[0].addr, data.len 117 | buffer.global_unlock 118 | discard format.uint.set_clipboard_data buffer 119 | close_clipboard() 120 | 121 | proc clear*(Δ) {.inline.} = 122 | clip.set_data_list 123 | 124 | proc get_data*(Δ, format): clip.fragment {.inline.} = 125 | clip.get_data_list(format)[0] 126 | 127 | proc set_data*(Δ, format; data: seq[byte]) {.inline.} = 128 | clip.set_data_list (format, data) 129 | 130 | proc set_data*(Δ; fragment: clip.fragment) {.inline.} = 131 | clip.set_data_list (fragment.format, fragment.data) 132 | 133 | proc contains_data*(Δ, format): bool {.inline.} = 134 | format.uint.clipboard_format_available != 0 135 | 136 | proc get_text*(Δ): string {.inline.} = 137 | return $clip.get_data clip.formats.unicode_text 138 | 139 | proc set_text*(Δ; text: string) {.inline.} = 140 | clip.set_data text 141 | 142 | proc contains_text*(Δ): bool {.inline.} = 143 | clip.contains_data clip.formats.unicode_text 144 | 145 | proc get_file_drop_list*(Δ): seq[string] {.inline.}= 146 | clip.get_data clip.formats.file_drop 147 | 148 | proc set_file_drop_list*(Δ; list: seq[string]) {.inline.} = 149 | clip.set_data list 150 | 151 | proc contains_file_drop_list*(Δ): bool {.inline.} = 152 | clip.contains_data clip.formats.file_drop 153 | #.} 154 | 155 | # ==Testing code== 156 | when isMainModule: include "../examples/basic.nim" --------------------------------------------------------------------------------