Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#75 destroy hooks #81

Merged
merged 20 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2baeccd
debug attempt
PhilippMDoerner Sep 13, 2023
dec9d45
Merge branch 'can-lehmann:main' into main
PhilippMDoerner Sep 13, 2023
6c6b974
#75 initial value based semantics version for new destructors
PhilippMDoerner Sep 14, 2023
fbb70a9
#75 second attempt using pointer semantics
PhilippMDoerner Sep 15, 2023
ecf8f6d
Add =sink hook and change GdkPixBuf to GdkPixbuf
can-lehmann Sep 15, 2023
cb5380c
Revert "Add =sink hook and change GdkPixBuf to GdkPixbuf"
PhilippMDoerner Sep 16, 2023
b8cbe1a
Revert "#75 second attempt using pointer semantics"
PhilippMDoerner Sep 16, 2023
9c1bbaa
#75 Third approach for destructors
PhilippMDoerner Sep 16, 2023
d532c91
#75 remove pointless docs.yml change
PhilippMDoerner Sep 16, 2023
7105001
#75 Make examples compile against project owlkettle version
PhilippMDoerner Sep 16, 2023
58af7b5
#82 Add custom pragma "locker" to disguise "Locks:0"
PhilippMDoerner Sep 16, 2023
cb8a324
Nim 2.0 desires its destructor hooks to be defined as
PhilippMDoerner Sep 16, 2023
f83bfa3
#76 Add destructors for Stylesheet
PhilippMDoerner Sep 16, 2023
3247d47
#82 Refactor how locker pragma gets generated and provided
PhilippMDoerner Sep 16, 2023
417b6d6
#82 Add doc comment
PhilippMDoerner Sep 16, 2023
f886994
#82 Remove now pointless file
PhilippMDoerner Sep 16, 2023
60223f3
#75 Refactor generating multi-nim-version hooks via template
PhilippMDoerner Sep 16, 2023
42f8eb2
#75 streamline construction of destructor objects
PhilippMDoerner Sep 16, 2023
b3482c1
#75 adjust indentation
PhilippMDoerner Sep 16, 2023
b20d10f
#75 add copy hook for TextBuffer
PhilippMDoerner Sep 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion owlkettle.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ task examples, "Build examples":
withDir "examples":
for file in findExamples("."):
echo "INFO: Compile " & file
exec "nim c --hints:off --verbosity:0 " & file
exec "nim c --hints:off --path:.. --verbosity:0 " & file
can-lehmann marked this conversation as resolved.
Show resolved Hide resolved
echo "INFO: OK"
echo "================================================================================"

Expand Down
8 changes: 8 additions & 0 deletions owlkettle/customPragmas.nim
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Definess a custom pragma called "locker".
## This does nothing when compiled with a nim version 2.0 or higher,
## but applies the "locks: 0" pragma if compiled for e.g. 1.6.X.
## "locks: 0" is applied to ensure that this code never accesses locked data in user-defined procs.
when NimMajor >= 2:
{.pragma: locker.}
else:
{.pragma: locker, locks: 0.}
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 7 additions & 5 deletions owlkettle/dataentries.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ when defined(nimPreviewSlimSystem):
import std/formatfloat
import widgets, widgetdef, guidsl

include ./customPragmas

when defined(owlkettleDocs) and isMainModule:
echo "# Dataentries Widgets\n\n"

Expand Down Expand Up @@ -145,19 +147,19 @@ method parse(entry: FormulaEntryState, text: string): (bool, float64) =
tokens: seq[Token]
cur: int

proc add(stream: var TokenStream, token: Token) {.locks: 0.} =
proc add(stream: var TokenStream, token: Token) {.locker.} =
stream.tokens.add(token)

proc next(stream: TokenStream, kind: TokenKind): bool {.locks: 0.} =
proc next(stream: TokenStream, kind: TokenKind): bool {.locker.} =
result = stream.cur < stream.tokens.len and
stream.tokens[stream.cur].kind == kind

proc take(stream: var TokenStream, kind: TokenKind): bool {.locks: 0.} =
proc take(stream: var TokenStream, kind: TokenKind): bool {.locker.} =
result = stream.next(kind)
if result:
stream.cur += 1

proc tokenize(text: string): TokenStream {.locks: 0.} =
proc tokenize(text: string): TokenStream {.locker.} =
const
WHITESPACE = {' ', '\n', '\r', '\t'}
OP = {'+', '-', '*', '/', '^', '%'}
Expand Down Expand Up @@ -191,7 +193,7 @@ method parse(entry: FormulaEntryState, text: string): (bool, float64) =
kind = TokenNumber
result.add(Token(kind: kind, value: name))

proc eval(stream: var TokenStream, level: int): tuple[valid: bool, val: float64] {.locks: 0.} =
proc eval(stream: var TokenStream, level: int): tuple[valid: bool, val: float64] {.locker.} =
var prefix = 1.0
if stream.take(TokenPrefixOp) and stream.tokens[stream.cur - 1].value == "-":
prefix = -1.0
Expand Down
1 change: 1 addition & 0 deletions owlkettle/gtk.nim
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ proc g_signal_connect_data*(widget: pointer,
proc g_type_from_name*(name: cstring): GType
proc g_object_new*(typ: GType): pointer {.varargs.}
proc g_object_ref*(obj: pointer)
proc g_object_weak_ref*(obj: pointer, notify: proc (data, oldObjectPtr: pointer) {.cdecl.}, data: pointer)
can-lehmann marked this conversation as resolved.
Show resolved Hide resolved
proc g_object_unref*(obj: pointer)
proc g_object_set_property*(obj: pointer, name: cstring, value: ptr GValue)
proc g_type_fundamental*(id: GType): GType
Expand Down
33 changes: 30 additions & 3 deletions owlkettle/mainloop.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,36 @@

import gtk, widgetdef

type Stylesheet* = ref object
provider: GtkCssProvider
priority: int
type
StylesheetObj = object
provider: GtkCssProvider
priority: int

Stylesheet* = ref StylesheetObj

when(NimMajor >= 2):
proc `=destroy`(stylesheet: StylesheetObj) =
if isNil(stylesheet.provider):
return

g_object_unref(pointer(stylesheet.provider))
else:
proc `=destroy`(stylesheet: var StylesheetObj) =
if isNil(stylesheet.provider):
return

g_object_unref(pointer(stylesheet.provider))
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved
proc `=copy`*(dest: var StylesheetObj, source: StylesheetObj) =
let areSameObject = pointer(source.provider) == pointer(dest.provider)
if areSameObject:
return

`=destroy`(dest)
wasMoved(dest)
if not isNil(source.provider):
g_object_ref(pointer(source.provider))

dest.provider = source.provider

const DEFAULT_PRIORITY = 600

Expand Down
70 changes: 55 additions & 15 deletions owlkettle/widgets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import std/[unicode, sets, tables, options, asyncfutures, hashes, times]
when defined(nimPreviewSlimSystem):
import std/assertions
import gtk, widgetdef, cairo, widgetutils

include ./customPragmas
when defined(owlkettleDocs) and isMainModule:
echo "# Widgets"

Expand Down Expand Up @@ -458,18 +458,48 @@ type
Colorspace* = enum
ColorspaceRgb

Pixbuf* = ref object
# Wrapper for the GdkPixbuf pointer to work with destructors of nim's ARC/ORC
# Todo: As of 16.09.2023 it is mildly buggy to try and to `PixBuf = distinct GdkPixbuf` and
# have destructors act on that new type directly. It was doable as shown in Ticket #75 and Github PR #81
# But required a =sink hook for no understandable reason *and* seemed risky due to bugs likely making it unstable.
# The pointer wrapped by intermediate type used as ref-type via an alias approach seems more stable for now.
# Re-evaluate this in Match 2024 to see whether we can remove the wrapper obj.
PixbufObj* = object
gdk: GdkPixbuf

Pixbuf* = ref PixbufObj

proc finalizer(pixbuf: Pixbuf) =
g_object_unref(pointer(pixbuf.gdk))
when(NimMajor >= 2):
proc `=destroy`(pixbuf: PixbufObj) =
if isNil(pixbuf.gdk):
return

g_object_unref(pointer(pixbuf.gdk))
else:
proc `=destroy`(pixbuf: var PixbufObj) =
if isNil(pixbuf.gdk):
return

g_object_unref(pointer(pixbuf.gdk))

proc `=copy`*(dest: var PixbufObj, source: PixbufObj) =
let areSameObject = pointer(source.gdk) == pointer(dest.gdk)
if areSameObject:
return

`=destroy`(dest)
wasMoved(dest)
if not isNil(source.gdk):
g_object_ref(pointer(source.gdk))

dest.gdk = source.gdk

proc newPixbuf(gdk: GdkPixbuf): Pixbuf =
if gdk.isNil:
raise newException(ValueError, "Unable to create Pixbuf from GdkPixbuf(nil)")
new(result, finalizer=finalizer)
result = Pixbuf()
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved
result.gdk = gdk
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved

proc newPixbuf*(width, height: int,
bitsPerSample: int = 8,
hasAlpha: bool = false,
Expand Down Expand Up @@ -549,7 +579,7 @@ proc handlePixbufReady(stream: pointer, result: GAsyncResult, data: pointer) {.c
var error = GError(nil)
let pixbuf = gdk_pixbuf_new_from_stream_finish(result, error.addr)
if error.isNil:
future.complete(Pixbuf(gdk: pixbuf))
future.complete(newPixbuf(pixbuf))
else:
let message = $error[].message
future.fail(newException(IoError, "Unable to load pixbuf: " & message))
Expand Down Expand Up @@ -793,7 +823,7 @@ proc `valIcon=`*(button: Button, name: string) =
proc updateChild*(state: Renderable,
child: var BoxChild[WidgetState],
updater: BoxChild[Widget],
setChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
setChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
if updater.widget.isNil:
if not child.widget.isNil:
child.widget = nil
Expand Down Expand Up @@ -1105,9 +1135,9 @@ type PanedChild[T] = object
proc buildPanedChild(child: PanedChild[Widget],
app: Viewable,
internalWidget: GtkWidget,
setChild: proc(paned, child: GtkWidget) {.cdecl, locks: 0.},
setResize: proc(paned: GtkWidget, val: cbool) {.cdecl, locks: 0.},
setShrink: proc(paned: GtkWidget, val: cbool) {.cdecl, locks: 0.}): PanedChild[WidgetState] =
setChild: proc(paned, child: GtkWidget) {.cdecl, locker.},
setResize: proc(paned: GtkWidget, val: cbool) {.cdecl, locker.},
setShrink: proc(paned: GtkWidget, val: cbool) {.cdecl, locker.}): PanedChild[WidgetState] =
child.widget.assignApp(app)
result = PanedChild[WidgetState](
widget: child.widget.build(),
Expand Down Expand Up @@ -1986,6 +2016,12 @@ type
underline*: Option[UnderlineKind]
style*: Option[CairoFontSlant]

# Wrapper for the GtkTextBuffer pointer to work with destructors of nim's ARC/ORC
# Todo: As of 16.09.2023 it is mildly buggy to try and to `TextBuffer = distinct GtkTextBuffer` and
# have destructors act on that new type directly. It was doable as shown in Ticket #75 and Github PR #81
# But required a =sink hook for no understandable reason *and* seemed risky due to bugs likely making it unstable.
# The pointer wrapped by intermediate type used as ref-type via an alias approach seems more stable for now.
# Re-evaluate this in Match 2024 to see whether we can remove the wrapper obj.
TextBufferObj = object
gtk: GtkTextBuffer

Expand All @@ -1995,11 +2031,15 @@ type
TextTag* = GtkTextTag
TextSlice* = HSlice[TextIter, TextIter]

proc finalizer(buffer: TextBuffer) =
g_object_unref(pointer(buffer.gtk))
when(NimMajor >= 2):
proc `=destroy`(buffer: TextBufferObj) =
g_object_unref(pointer(buffer.gtk))
else:
proc `=destroy`(buffer: var TextBufferObj) =
g_object_unref(pointer(buffer.gtk))

proc newTextBuffer*(): TextBuffer =
new(result, finalizer=finalizer)
new(result)
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved
result.gtk = gtk_text_buffer_new(nil.GtkTextTagTable)
PhilippMDoerner marked this conversation as resolved.
Show resolved Hide resolved

{.push hint[Name]: off.}
Expand Down Expand Up @@ -2839,7 +2879,7 @@ renderable ContextMenu:

hooks menu:
(build, update):
proc replace(box, oldMenu, newMenu: GtkWidget) {.locks: 0.} =
proc replace(box, oldMenu, newMenu: GtkWidget) {.locker.} =
if not oldMenu.isNil:
gtk_widget_remove_controller(box, state.controller)
state.controller = GtkEventController(nil)
Expand Down
23 changes: 12 additions & 11 deletions owlkettle/widgetutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import std/[sets]
import gtk, widgetdef
include ./customPragmas

proc redraw*[T](event: EventObj[T]) =
if event.app.isNil:
Expand Down Expand Up @@ -74,7 +75,7 @@ proc updateStyle*[State, Widget](state: State, widget: Widget) {.inline.} =
proc updateChild*(state: Renderable,
child: var WidgetState,
updater: Widget,
replaceChild: proc(widget, oldChild, newChild: GtkWidget) {.locks: 0.}) =
replaceChild: proc(widget, oldChild, newChild: GtkWidget) {.locker.}) =
if updater.isNil:
if not child.isNil:
replaceChild(state.internalWidget, unwrapInternalWidget(child), nil.GtkWidget)
Expand All @@ -92,8 +93,8 @@ proc updateChild*(state: Renderable,
proc updateChild*(state: Renderable,
child: var WidgetState,
updater: Widget,
addChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
addChild: proc(widget, child: GtkWidget) {.cdecl, locker.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
proc replace(widget, oldChild, newChild: GtkWidget) =
if not oldChild.isNil:
removeChild(widget, oldChild)
Expand All @@ -105,7 +106,7 @@ proc updateChild*(state: Renderable,
proc updateChild*(state: Renderable,
child: var WidgetState,
updater: Widget,
setChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
setChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
proc replace(widget, oldChild, newChild: GtkWidget) =
setChild(widget, newChild)

Expand All @@ -114,8 +115,8 @@ proc updateChild*(state: Renderable,
proc updateChildren*(state: Renderable,
children: var seq[WidgetState],
updates: seq[Widget],
addChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
addChild: proc(widget, child: GtkWidget) {.cdecl, locker.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
updates.assignApp(state.app)

var
Expand Down Expand Up @@ -151,9 +152,9 @@ proc updateChildren*(state: Renderable,
proc updateChildren*(state: Renderable,
children: var seq[WidgetState],
updates: seq[Widget],
addChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.},
insertChild: proc(widget, child: GtkWidget, index: cint) {.cdecl, locks: 0.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
addChild: proc(widget, child: GtkWidget) {.cdecl, locker.},
insertChild: proc(widget, child: GtkWidget, index: cint) {.cdecl, locker.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
updates.assignApp(state.app)

var it = 0
Expand Down Expand Up @@ -196,8 +197,8 @@ proc toGtk*(align: Align): GtkAlign = GtkAlign(ord(align))
proc updateAlignedChildren*(state: Renderable,
children: var seq[AlignedChild[WidgetState]],
updates: seq[AlignedChild[Widget]],
addChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locks: 0.}) =
addChild: proc(widget, child: GtkWidget) {.cdecl, locker.},
removeChild: proc(widget, child: GtkWidget) {.cdecl, locker.}) =
updates.assignApp(state.app)
var
it = 0
Expand Down