tk_ccgo.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // Copyright 2024 The tk9.0-go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build linux && (386 || arm || loong64 || ppc64le || riscv64 || s390x)
  5. package tk9_0 // import "modernc.org/tk9.0"
  6. import (
  7. _ "embed"
  8. "errors"
  9. "fmt"
  10. "os"
  11. "path/filepath"
  12. "runtime"
  13. "modernc.org/libc"
  14. libtcl "modernc.org/libtcl9.0"
  15. tcllib "modernc.org/libtcl9.0/library"
  16. libtk "modernc.org/libtk9.0"
  17. tklib "modernc.org/libtk9.0/library"
  18. tcl "modernc.org/tcl9.0"
  19. "modernc.org/tk9.0/internal/img"
  20. )
  21. const (
  22. tclLibZip = "tcl_library.zip"
  23. tclLibMountPoint = "/lib/tcl"
  24. tkLibZip = "tk_library.zip"
  25. tkLibMountPoint = "/lib/tk"
  26. )
  27. var (
  28. interp *tcl.Interp
  29. shasig = map[string]string{
  30. // other
  31. "tcl_library.zip": "1849c8e8df2e23cdaf904bd04f1316be29473612a215e84c9f9f8ba144d16b2f",
  32. "tk_library.zip": "ea619ae0c921446db3659cbfc4efa2c700f2531c9a20ce9029b603b629c29711",
  33. }
  34. )
  35. func lazyInit() {
  36. if initialized {
  37. return
  38. }
  39. runtime.LockOSThread()
  40. initialized = true
  41. defer commonLazyInit()
  42. var cacheDir string
  43. if cacheDir, Error = getCacheDir(); Error != nil {
  44. return
  45. }
  46. tls := libc.NewTLS()
  47. zf1 := filepath.Join(cacheDir, tclLibZip)
  48. zf2 := filepath.Join(cacheDir, tkLibZip)
  49. var cs uintptr
  50. if cs, Error = libc.CString(fmt.Sprintf(`
  51. zipfs mount %s %s
  52. zipfs mount %s %s
  53. `, zf1, tclLibMountPoint, zf2, tkLibMountPoint)); Error != nil {
  54. return
  55. }
  56. p := libtcl.XTcl_SetPreInitScript(tls, cs)
  57. if p != 0 {
  58. panic(todo("Tcl_SetPreInitScript internal error: %s", libc.GoString(p)))
  59. }
  60. if interp, Error = tcl.NewInterp(map[string]string{
  61. "tcl_library": fmt.Sprintf("//zipfs:%s/library", tclLibMountPoint),
  62. "tk_library": fmt.Sprintf("//zipfs:%s/library", tkLibMountPoint),
  63. }); Error != nil {
  64. return
  65. }
  66. tls = interp.TLS()
  67. h := interp.Handle()
  68. if rc := libtk.XTk_Init(tls, h); rc != libtk.TCL_OK {
  69. interp.Close()
  70. Error = fmt.Errorf("failed to initialize the Tk subsystem")
  71. return
  72. }
  73. if Error = interp.RegisterCommand("eventDispatcher", eventDispatcher, nil, nil); Error == nil {
  74. setDefaults()
  75. }
  76. if rc := img.XTkimg_Init(tls, h); rc != 0 {
  77. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimg_Init")
  78. return
  79. }
  80. if rc := img.XJpegtcl_Init(tls, h); rc != 0 {
  81. Error = fmt.Errorf("failed to initialize the img subsystem: Jpegtcl_Init")
  82. return
  83. }
  84. if rc := img.XTkimgjpeg_Init(tls, h); rc != 0 {
  85. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgjpeg_Init")
  86. return
  87. }
  88. if rc := img.XTkimgbmp_Init(tls, h); rc != 0 {
  89. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgbmp_Init")
  90. return
  91. }
  92. if rc := img.XTkimgico_Init(tls, h); rc != 0 {
  93. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgico_Init")
  94. return
  95. }
  96. if rc := img.XTkimgpcx_Init(tls, h); rc != 0 {
  97. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgpcx_Init")
  98. return
  99. }
  100. if rc := img.XTkimgxpm_Init(tls, h); rc != 0 {
  101. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgxpm_Init")
  102. return
  103. }
  104. if rc := img.XZlibtcl_Init(tls, h); rc != 0 {
  105. Error = fmt.Errorf("failed to initialize the img subsystem: Zlibtcl_Init")
  106. return
  107. }
  108. if rc := img.XPngtcl_Init(tls, h); rc != 0 {
  109. Error = fmt.Errorf("failed to initialize the img subsystem: Pngtcl_Init")
  110. return
  111. }
  112. if rc := img.XTkimgpng_Init(tls, h); rc != 0 {
  113. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgpng_Init")
  114. return
  115. }
  116. if rc := img.XTkimgppm_Init(tls, h); rc != 0 {
  117. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgppm_Init")
  118. return
  119. }
  120. if rc := img.XTkimgtga_Init(tls, h); rc != 0 {
  121. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgtga_Init")
  122. return
  123. }
  124. if rc := img.XTifftcl_Init(tls, h); rc != 0 {
  125. Error = fmt.Errorf("failed to initialize the img subsystem: Tifftcl_Init")
  126. return
  127. }
  128. if rc := img.XTkimgtiff_Init(tls, h); rc != 0 {
  129. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgtiff_Init")
  130. return
  131. }
  132. if rc := img.XTkimgxbm_Init(tls, h); rc != 0 {
  133. Error = fmt.Errorf("failed to initialize the img subsystem: Tkimgxbm_Init")
  134. return
  135. }
  136. }
  137. func getCacheDir() (r string, err error) {
  138. if r, err = os.UserCacheDir(); err != nil {
  139. return "", err
  140. }
  141. r0 := filepath.Join(r, "modernc.org", libVersion, goos)
  142. r = filepath.Join(r0, goarch)
  143. fi, err := os.Stat(r)
  144. if err == nil && fi.IsDir() {
  145. if checkSig(r, shasig) {
  146. return r, nil
  147. }
  148. os.RemoveAll(r) // Tampered or corrupted.
  149. }
  150. os.MkdirAll(r0, 0700)
  151. tmp, err := os.MkdirTemp(r0, "")
  152. if err != nil {
  153. return "", err
  154. }
  155. zf := filepath.Join(tmp, tclLibZip)
  156. if err = os.WriteFile(zf, []byte(tcllib.Zip), 0660); err != nil {
  157. return "", err
  158. }
  159. zf = filepath.Join(tmp, tkLibZip)
  160. if err = os.WriteFile(zf, []byte(tklib.Zip), 0660); err != nil {
  161. return "", err
  162. }
  163. if err = os.Rename(tmp, r); err == nil {
  164. return r, nil
  165. }
  166. cleanupDirs = append(cleanupDirs, tmp)
  167. return tmp, nil
  168. }
  169. func eval(code string) (r string, err error) {
  170. if dmesgs {
  171. defer func() {
  172. dmesg("code=%s -> r=%v err=%v", code, r, err)
  173. }()
  174. }
  175. if !initialized {
  176. lazyInit()
  177. if Error != nil {
  178. return "", Error
  179. }
  180. }
  181. return interp.Eval(code, tcl.EvalDirect)
  182. }
  183. func eventDispatcher(data any, interp *tcl.Interp, argv []string) int {
  184. id, e, err := newEvent(argv[1])
  185. if err != nil {
  186. interp.SetResult(fmt.Sprintf("eventDispatcher internal error: argv1=`%s`", argv[1]))
  187. return tcl_error
  188. }
  189. h := handlers[int32(id)]
  190. e.W = h.w
  191. if len(argv) > 2 { // eg.: ["eventDispatcher", "42", "0.1", "0.9"]
  192. e.args = argv[2:]
  193. }
  194. switch h.callback(e); {
  195. case e.Err != nil:
  196. interp.SetResult(tclSafeString(e.Err.Error()))
  197. return libtcl.TCL_ERROR
  198. default:
  199. interp.SetResult(e.Result)
  200. return e.returnCode
  201. }
  202. }
  203. // Finalize releases all resources held, if any. This may include temporary
  204. // files. Finalize is intended to be called on process shutdown only.
  205. func Finalize() (err error) {
  206. if finished.Swap(1) != 0 {
  207. return
  208. }
  209. defer runtime.UnlockOSThread()
  210. if interp != nil {
  211. err = interp.Close()
  212. interp = nil
  213. }
  214. for _, v := range cleanupDirs {
  215. err = errors.Join(err, os.RemoveAll(v))
  216. }
  217. return err
  218. }
  219. func setResult(s string) (err error) {
  220. return interp.SetResult(s)
  221. }
  222. func cString(s string) (r uintptr, err error) {
  223. return libc.CString(s)
  224. }
  225. func callSplitList(cList uintptr, argcPtr uintptr, argvPtr uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
  226. rc := libtcl.XTclSplitList(interp.TLS(), interp.Handle(), cList, argcPtr, argvPtr) // .SyscallN(splitListProc, interp, cList, argcPtr, argvPtr)
  227. if rc == tcl_error {
  228. err = libtcl.TCL_ERROR
  229. }
  230. return uintptr(rc), 0, err
  231. }
  232. var oom = errors.New("OOM")
  233. // Internal malloc enabling parseList() in tk.go to not care about the target
  234. // specific implemetations.
  235. func malloc(sz int) (r uintptr, err error) {
  236. if r = libc.Xmalloc(interp.TLS(), libc.Tsize_t(sz)); r == 0 {
  237. err = oom
  238. }
  239. return r, err
  240. }
  241. // Internal free enabling parseList() in tk.go to not care about the target
  242. // specific implemetations.
  243. func free(p uintptr) (err error) {
  244. libc.Xfree(interp.TLS(), p)
  245. return nil
  246. }