M doc/doctools/gen/irc.man => doc/doctools/gen/irc.man +68 -0
@@ 1,5 1,73 @@
[manpage_begin irc tclircc 0.0.1]
[titledesc {Library irc.tcl}]
[description]
+[para]
+This is a to-spec IRCv3 client library.
+It is designed to be freely extracted.
+[section {Getting a channel}]
+[file irc.tcl] provides 2 ways to connect to an IRC server.
+[subsection [concat [cmd irc::connect] [arg hostname] [arg port] [opt [arg usetls]]]]
+
+Connects to [arg hostname]:[arg port], and sets up all the necessary state.
+If [arg usetls] is set to true, the [package tls] module will be used to connect,
+instead of the builtin [cmd socket] command. If unset, [cmd socket] will be used.
+
+[para]
+[sectref {Channel metadata}] is initialized with [arg proto], [arg hostname], [arg port], and [arg uri] set.
+[subsection [concat [cmd irc::enroll] [arg chan] [opt [arg meta]]]]
+
+Sets up the internal state necessary for chan to be used as an IRC socket.
+It is called internally by [cmd irc::connect].
+This command is exposed for the use-case where an IRC channel might have a more bespoke acquisition process than a simple socket connection.
+
+[para]
+[arg meta] is the initial state of the [sectref {channel metadata}].
+[section {Listening to it}]
+[file irc.tcl] provides an event dispatch system, via a [cmd fileevent] script registered on the IRC channel.
+Events are dispatched by matching their patterns against incoming messages.
+[subsection [concat [cmd irc::listen] [arg subcommand] [arg chan]]]
+Enable or disable the [cmd fileevent] script for the dispatch system.
+
+[list_begin definitions]
+[call [cmd irc::listen] [const on] [arg chan]]
+Apply the [cmd fileevent] wrapper to [arg chan].
+Returns the previous [cmd fileevent] wrapper.
+[call [cmd irc::listen] [const off] [arg chan]]
+Remove the [cmd fileevent] wrapper from [arg chan].
+Errors if the channel does not currently have the irc handler set.
+[list_end]
+[subsection [concat [cmd irc::listener] [arg subcommand] [arg chan] [opt [arg arg]...]]]
+Configure [cmd listener]-type event handlers.
+[para]
+[cmd listener]-type handlers are scripts, executed in either a sub-interpreter or a seperate thread.
+Interpreter listeners should yield themselves with [cmd after] during long operations, and never block for extended periods.
+If that is infeasible, threaded listeners are recommended.
+[para]
+[cmd listener]-type scripts are spawned with the variable [var dispatch] set to the channel they are to recieve dispatches over.
+Each line will be a [cmd dict] with contents as described in [sectref {Event Dispatch Contents}].
+Interpreters are given access to the [sectref {Dispatch-aliased IRC commands}], while threads are given the variable [var parent], the ID of the main thread.
+[para]
+When a listener is removed, it will recieve a message of just [const end].
+It should perform necessary cleanup quickly, as the application is likely exiting.
+Threads may simply [cmd thread::release] themselves, while interps may call the provided [cmd selfdestruct].
+
+[list_begin definitions]
+[call [cmd irc::listener] [const add] [arg chan] [opt [option -thread]] [arg patlist] [arg script]]
+Registers [arg script] as a [cmd listener]-type handler on [arg chan], using [arg patlist] as the [sectref {Message Pattern List}].
+Returns an id that can be passed to [cmd irc::listener] [const remove] or [cmd irc::patlist].
+[para]
+If [option -thread] is passed, it will be created as a threaded listener, otherwise it will be created in a sub-interpreter.
+[call [cmd irc::listener] [const remove] [arg chan] [arg id]]
+Unregisters the listener identified by [arg id] from [arg chan].
+[para]
+Ignores requests for nonexistent handlers or handlers of the wrong type.
+[list_end]
+[subsection [concat [cmd irc::handler] [arg subcommand] [arg chan] [opt [arg arg]...]]]
+
+[subsection [concat [cmd irc::is] [arg type] [arg value]]]
+Validation helper.
+
+[para]
+
[manpage_end]
M doc/doctools/gen/main.man => doc/doctools/gen/main.man +12 -0
@@ 1,5 1,17 @@
[manpage_begin main_thread tclircc 0.0.1]
[titledesc {Thread main}]
[description]
+[para]
+This is the application entrypoint. It does the following.
+[list_begin itemized]
+[item]
+brings up the routing system
+[item]
+brings up the database
+[item]
+loads the other core threads
+[item]
+opens a window
+[list_end]
[manpage_end]
R doc/doctools/gen/threads.man => doc/doctools/gen/router.man +2 -2
@@ 1,5 1,5 @@
-[manpage_begin threads tclircc 0.0.1]
-[titledesc {Component threads.tcl}]
+[manpage_begin router tclircc 0.0.1]
+[titledesc {Component router.tcl}]
[description]
[manpage_end]
M doc/md/irc.md => doc/md/irc.md +114 -0
@@ 14,6 14,120 @@ irc \- Library irc\.tcl
- [Table Of Contents](#toc)
+ - [Synopsis](#synopsis)
+
- [Description](#section1)
+ - [Getting a channel](#section2)
+
+ - [__irc::connect__ *hostname* *port*
+ ?*usetls*?](#subsection1)
+
+ - [__irc::enroll__ *chan* ?*meta*?](#subsection2)
+
+ - [Listening to it](#section3)
+
+ - [__irc::listen__ *subcommand* *chan*](#subsection3)
+
+ - [__irc::listener__ *subcommand* *chan*
+ ?*arg*\.\.\.?](#subsection4)
+
+ - [__irc::handler__ *subcommand* *chan*
+ ?*arg*\.\.\.?](#subsection5)
+
+ - [__irc::is__ *type* *value*](#subsection6)
+
+# <a name='synopsis'></a>SYNOPSIS
+
+[__irc::listen__ __on__ *chan*](#1)
+[__irc::listen__ __off__ *chan*](#2)
+[__irc::listener__ __add__ *chan* ?__\-thread__? *patlist* *script*](#3)
+[__irc::listener__ __remove__ *chan* *id*](#4)
+
# <a name='description'></a>DESCRIPTION
+
+This is a to\-spec IRCv3 client library\. It is designed to be freely extracted\.
+
+# <a name='section2'></a>Getting a channel
+
+"irc\.tcl" provides 2 ways to connect to an IRC server\.
+
+## <a name='subsection1'></a>__irc::connect__ *hostname* *port* ?*usetls*?
+
+Connects to *hostname*:*port*, and sets up all the necessary state\. If
+*usetls* is set to true, the __tls__ module will be used to connect,
+instead of the builtin __socket__ command\. If unset, __socket__ will be
+used\.
+
+__Channel metadata__ is initialized with *proto*, *hostname*, *port*,
+and *uri* set\.
+
+## <a name='subsection2'></a>__irc::enroll__ *chan* ?*meta*?
+
+Sets up the internal state necessary for chan to be used as an IRC socket\. It is
+called internally by __irc::connect__\. This command is exposed for the
+use\-case where an IRC channel might have a more bespoke acquisition process than
+a simple socket connection\.
+
+*meta* is the initial state of the __channel metadata__\.
+
+# <a name='section3'></a>Listening to it
+
+"irc\.tcl" provides an event dispatch system, via a __fileevent__ script
+registered on the IRC channel\. Events are dispatched by matching their patterns
+against incoming messages\.
+
+## <a name='subsection3'></a>__irc::listen__ *subcommand* *chan*
+
+Enable or disable the __fileevent__ script for the dispatch system\.
+
+ - <a name='1'></a>__irc::listen__ __on__ *chan*
+
+ Apply the __fileevent__ wrapper to *chan*\. Returns the previous
+ __fileevent__ wrapper\.
+
+ - <a name='2'></a>__irc::listen__ __off__ *chan*
+
+ Remove the __fileevent__ wrapper from *chan*\. Errors if the channel
+ does not currently have the irc handler set\.
+
+## <a name='subsection4'></a>__irc::listener__ *subcommand* *chan* ?*arg*\.\.\.?
+
+Configure __listener__\-type event handlers\.
+
+__listener__\-type handlers are scripts, executed in either a sub\-interpreter
+or a seperate thread\. Interpreter listeners should yield themselves with
+__after__ during long operations, and never block for extended periods\. If
+that is infeasible, threaded listeners are recommended\.
+
+__listener__\-type scripts are spawned with the variable __dispatch__ set
+to the channel they are to recieve dispatches over\. Each line will be a
+__dict__ with contents as described in __Event Dispatch Contents__\.
+Interpreters are given access to the __Dispatch\-aliased IRC commands__,
+while threads are given the variable __parent__, the ID of the main thread\.
+
+When a listener is removed, it will recieve a message of just __end__\. It
+should perform necessary cleanup quickly, as the application is likely exiting\.
+Threads may simply __thread::release__ themselves, while interps may call
+the provided __selfdestruct__\.
+
+ - <a name='3'></a>__irc::listener__ __add__ *chan* ?__\-thread__? *patlist* *script*
+
+ Registers *script* as a __listener__\-type handler on *chan*, using
+ *patlist* as the __Message Pattern List__\. Returns an id that can be
+ passed to __irc::listener__ __remove__ or __irc::patlist__\.
+
+ If __\-thread__ is passed, it will be created as a threaded listener,
+ otherwise it will be created in a sub\-interpreter\.
+
+ - <a name='4'></a>__irc::listener__ __remove__ *chan* *id*
+
+ Unregisters the listener identified by *id* from *chan*\.
+
+ Ignores requests for nonexistent handlers or handlers of the wrong type\.
+
+## <a name='subsection5'></a>__irc::handler__ *subcommand* *chan* ?*arg*\.\.\.?
+
+## <a name='subsection6'></a>__irc::is__ *type* *value*
+
+Validation helper\.
M doc/md/main.md => doc/md/main.md +10 -0
@@ 17,3 17,13 @@ main\_thread \- Thread main
- [Description](#section1)
# <a name='description'></a>DESCRIPTION
+
+This is the application entrypoint\. It does the following\.
+
+ - brings up the routing system
+
+ - brings up the database
+
+ - loads the other core threads
+
+ - opens a window
R doc/md/threads.md => doc/md/router.md +3 -3
@@ 2,13 2,13 @@
toc: false
---
-[//000000001]: # (threads \- )
+[//000000001]: # (router \- )
[//000000002]: # (Generated from file 'doctools' by tcllib/doctools with format 'markdown')
-[//000000003]: # (threads\(tclircc\) 0\.0\.1 "")
+[//000000003]: # (router\(tclircc\) 0\.0\.1 "")
# NAME
-threads \- Component threads\.tcl
+router \- Component router\.tcl
# <a name='toc'></a>Table Of Contents
M src/irc.tcl => src/irc.tcl +248 -167
@@ 2,6 2,9 @@
# [manpage_begin irc tclircc 0.0.1]
# [titledesc {Library irc.tcl}]
# [description]
+# [para]
+# This is a to-spec IRCv3 client library.
+# It is designed to be freely extracted.
# handler types:
# chan <dispatch> <interp> // irc::listener
@@ 21,6 24,10 @@
package require Thread
+#***
+# [section {Getting a channel}]
+# [file irc.tcl] provides 2 ways to connect to an IRC server.
+
namespace eval ::irc {
variable log [logger::init irc]
variable logd [logger::init irc::dispatch]
@@ 29,8 36,247 @@ namespace eval ::irc {
variable chan.handlers
variable chan.interceptors
- # documented
- proc is {type value {cap {}}} {
+ #***
+ # [subsection [concat [cmd irc::connect] [arg hostname] [arg port] [opt [arg usetls]]]]
+ #
+ # Connects to [arg hostname]:[arg port], and sets up all the necessary state.
+ # If [arg usetls] is set to true, the [package tls] module will be used to connect,
+ # instead of the builtin [cmd socket] command. If unset, [cmd socket] will be used.
+ #
+ # [para]
+ # [sectref {Channel metadata}] is initialized with [arg proto], [arg hostname], [arg port], and [arg uri] set.
+ proc connect {hostname port {usetls 0}} {
+ if $usetls {
+ if {[info commands ::tls::socket] == ""} { package require tls }
+ set chan [::tls::socket $hostname $port]
+ set proto ircs
+ } else {
+ set chan [socket $hostname $port]
+ set proto irc
+ }
+
+ irc::enroll $chan [dict create uri $proto://$hostname:$port \
+ proto $proto \
+ hostname $hostname \
+ port $port]
+
+ return $chan
+ }
+
+ #***
+ # [subsection [concat [cmd irc::enroll] [arg chan] [opt [arg meta]]]]
+ #
+ # Sets up the internal state necessary for chan to be used as an IRC socket.
+ # It is called internally by [cmd irc::connect].
+ # This command is exposed for the use-case where an IRC channel might have a more bespoke acquisition process than a simple socket connection.
+ #
+ # [para]
+ # [arg meta] is the initial state of the [sectref {channel metadata}].
+ proc enroll {chan {meta {}}} {
+ variable chan.meta
+ variable chan.handlers
+ variable chan.interceptors
+ fconfigure $chan -translation crlf -blocking 0a
+ set chan.meta($chan) $meta
+ set chan.handlers($chan) {}
+ set chan.interceptors($chan) {}
+ }
+
+
+ #***
+ # [section {Listening to it}]
+ # [file irc.tcl] provides an event dispatch system, via a [cmd fileevent] script registered on the IRC channel.
+ # Events are dispatched by matching their patterns against incoming messages.
+
+ #***
+ # [subsection [concat [cmd irc::listen] [arg subcommand] [arg chan]]]
+ # Enable or disable the [cmd fileevent] script for the dispatch system.
+ #
+ # [list_begin definitions]
+ proc listen {subcommand chan} {
+ switch -- $subcommand {
+ on {
+ #***
+ # [call [cmd irc::listen] [const on] [arg chan]]
+ # Apply the [cmd fileevent] wrapper to [arg chan].
+ # Returns the previous [cmd fileevent] wrapper.
+ fileevent $chan readable [list ::irc::int-onmsg $chan]
+ }
+ off {
+ #***
+ # [call [cmd irc::listen] [const off] [arg chan]]
+ # Remove the [cmd fileevent] wrapper from [arg chan].
+ # Errors if the channel does not currently have the irc handler set.
+ set oldfe [fileevent $chan readable]
+ if {[fileevent $chan readable] != [list ::irc::int-onmsg $chan]} {
+ return -code error "channel \"$chan\" not listening for irc"
+ } else { fileevent $chan readable "" }
+ }
+ default { return -code error "unknown subcommand \"$subcommand\": must be off or on" }
+ }
+ }
+ #***
+ # [list_end]
+
+ #***
+ # [subsection [concat [cmd irc::listener] [arg subcommand] [arg chan] [opt [arg arg]...]]]
+ # Configure [cmd listener]-type event handlers.
+ # [para]
+ # [cmd listener]-type handlers are scripts, executed in either a sub-interpreter or a seperate thread.
+ # Interpreter listeners should yield themselves with [cmd after] during long operations, and never block for extended periods.
+ # If that is infeasible, threaded listeners are recommended.
+ # [para]
+ # [cmd listener]-type scripts are spawned with the variable [var dispatch] set to the channel they are to recieve dispatches over.
+ # Each line will be a [cmd dict] with contents as described in [sectref {Event Dispatch Contents}].
+ # Interpreters are given access to the [sectref {Dispatch-aliased IRC commands}], while threads are given the variable [var parent], the ID of the main thread.
+ # [para]
+ # When a listener is removed, it will recieve a message of just [const end].
+ # It should perform necessary cleanup quickly, as the application is likely exiting.
+ # Threads may simply [cmd thread::release] themselves, while interps may call the provided [cmd selfdestruct].
+ #
+ # [list_begin definitions]
+ proc listener {subcommand chan args} {
+ variable chan.handlers
+ switch -- $subcommand {
+ add {
+ #***
+ # [call [cmd irc::listener] [const add] [arg chan] [opt [option -thread]] [arg patlist] [arg script]]
+ # Registers [arg script] as a [cmd listener]-type handler on [arg chan], using [arg patlist] as the [sectref {Message Pattern List}].
+ # Returns an id that can be passed to [cmd irc::listener] [const remove] or [cmd irc::patlist].
+ # [para]
+ # If [option -thread] is passed, it will be created as a threaded listener, otherwise it will be created in a sub-interpreter.
+ set thread false
+ if {[lindex $args 0] == "-thread"} {
+ set thread true
+ set args [lrange $args 1 end]
+ }
+
+ if {[llength $args] != 2} { return -code error "wrong # args: should be \"irc::listener add chan ?-thread? patlist script\"" }
+ lassign $args patlist script
+ set id [format "%016x" [expr {round(rand() * (2**64))}]]
+
+ if !$thread {
+ set interp [interp create]
+ irc::int-setaliases $interp
+ interp share {} $chan $interp
+
+ lassign [chan pipe] reader writer
+ interp transfer {} $reader $interp
+ $interp alias selfdestruct ::irc::int-rminterp $interp
+ $interp eval [list set dispatch $reader]
+ $interp eval [list after idle $script]
+
+ lappend chan.handlers($chan) [list $patlist chan $id $writer $interp]
+ } else {
+ set thread [thread::create -preserved]
+ lassign [chan pipe] reader writer
+
+ thread::transfer $thread $reader
+ thread::send -async $thread [list set dispatch $reader]
+ thread::send -async $thread [list set parent [thread::id]]
+ thread::send -async $thread $script
+
+ lappend chan.handlers($chan) [list $patlist tchan $id $writer $thread]
+ }
+
+ return $id
+ }
+ remove {
+ #***
+ # [call [cmd irc::listener] [const remove] [arg chan] [arg id]]
+ # Unregisters the listener identified by [arg id] from [arg chan].
+ # [para]
+ # Ignores requests for nonexistent handlers or handlers of the wrong type.
+ if {[llength $args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" }
+ lassign $args rmid
+ set newlist ""
+ foreach handler [set chan.handlers($chan)] {
+ lassign $handler _ type handlerid writer iot
+ if {$handlerid != $rmid || $type ni {chan tchan}} {
+ lappend newlist $handler
+ } elseif {$type == "chan"} {
+ puts $writer end
+ flush $writer
+ } elseif {$type == "tchan"} {
+ puts $writer end
+ flush $writer
+ thread::release $iot
+ }
+ }
+ set chan.handlers($chan) $newlist
+ }
+ default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" }
+ }
+ }
+ #***
+ # [list_end]
+
+ #***
+ # [subsection [concat [cmd irc::handler] [arg subcommand] [arg chan] [opt [arg arg]...]]]
+ #
+ proc handler {subcommand chan args} {
+ variable chan.handlers
+ switch -- $subcommand {
+ add {
+ set thread false
+ if {[lindex $args 0] == "-thread"} {
+ set thread true
+ set args [lrange $args 1 end]
+ }
+
+ if {[llength $args] ni {2 3}} { return -code error "wrong # args: should be \"irc::handlers add chan ?-thread? patlist script ?interp-or-thread?\"" }
+ set iot [lassign $args patlist script]
+ set id [format "%016x" [expr {round(rand() * (2**64))}]]
+
+ if !$thread {
+ if [llength $iot] {
+ irc::int-setaliases {*}$iot
+ interp share {} $chan {*}$iot
+ }
+
+ lappend chan.handlers($chan) [list $patlist script $id $script {*}$iot]
+ } else {
+ if ![llength $iot] {
+ set iot [list [thread::create -preserved]]
+ } else {
+ thread::preserve {*}$iot
+ }
+
+ thread::send -async $iot [list set parent [thread::id]]
+
+ lappend chan.handlers($chan) [list $patlist tscript $id $script {*}$iot]
+ }
+
+ return $id
+ }
+ remove {
+ if {[llength args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" }
+ lassign $args rmid
+ set newlist ""
+ foreach handler [set chan.handlers($chan)] {
+ set iot [lassign $handler _ type handlerid _]
+ if {$handlerid != $rmid || $type ni {script tscript}} {
+ lappend newlist $handler
+ } elseif {$type == "script" && [llength $iot]} {
+ interp delete {*}$iot
+ } elseif {$type == "tscript"} {
+ thread::release {*}$iot
+ }
+ }
+ set chan.handlers($chan) $newlist
+ }
+ default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" }
+ }
+ }
+
+
+ #***
+ # [subsection [concat [cmd irc::is] [arg type] [arg value]]]
+ # Validation helper.
+ #
+ # [para]
+ #
+ proc is {type value} {
# validation helper.
# cap is a list of negotiated capabilities.
switch -- $type {
@@ 111,52 357,6 @@ namespace eval ::irc {
}
}
- # documented
- proc connect {hostname port {usetls 0}} {
- if $usetls {
- if {[info commands ::tls::socket] == ""} { package require tls }
- set chan [::tls::socket $hostname $port]
- set proto ircs
- } else {
- set chan [socket $hostname $port]
- set proto irc
- }
-
- irc::enroll $chan [dict create uri $proto://$hostname:$port \
- proto $proto \
- hostname $hostname \
- port $port]
-
- return $chan
- }
-
- # documented
- proc enroll {chan {meta {}}} {
- variable chan.meta
- variable chan.handlers
- variable chan.interceptors
- fconfigure $chan -translation crlf -blocking 0
- set chan.meta($chan) $meta
- set chan.handlers($chan) {}
- set chan.interceptors($chan) {}
- }
-
- # documented
- proc listen {subcommand chan} {
- switch -- $subcommand {
- on {
- fileevent $chan readable [list ::irc::int-onmsg $chan]
- }
- off {
- set oldfe [fileevent $chan readable]
- if {[fileevent $chan readable] != [list ::irc::int-onmsg $chan]} {
- return -code error "channel \"$chan\" not listening for irc"
- } else { fileevent $chan readable "" }
- }
- default { return -code error "unknown subcommand \"$subcommand\": must be off or on" }
- }
- }
-
# nodoc
# helper function that rebrands dict command errors
proc int-dictsub args {
@@ 322,125 522,6 @@ namespace eval ::irc {
proc int-rminterp {interp} {
interp delete $interp
}
- # documented
- proc listener {subcommand chan args} {
- variable chan.handlers
- switch -- $subcommand {
- add {
- set thread false
- if {[lindex $args 0] == "-thread"} {
- set thread true
- set args [lrange $args 1 end]
- }
-
- if {[llength $args] != 2} { return -code error "wrong # args: should be \"irc::listener add chan ?-thread? patlist script\"" }
- lassign $args patlist script
- set id [format "%016x" [expr {round(rand() * (2**64))}]]
-
- if !$thread {
- set interp [interp create]
- irc::int-setaliases $interp
- interp share {} $chan $interp
-
- lassign [chan pipe] reader writer
- interp transfer {} $reader $interp
- $interp alias selfdestruct ::irc::int-rminterp $interp
- $interp eval [list set dispatch $reader]
- $interp eval [list after idle $script]
-
- lappend chan.handlers($chan) [list $patlist chan $id $writer $interp]
- } else {
- set thread [thread::create -preserved]
- lassign [chan pipe] reader writer
-
- thread::transfer $thread $reader
- thread::send -async $thread [list set dispatch $reader]
- thread::send -async $thread [list set parent [thread::id]]
- thread::send -async $thread $script
-
- lappend chan.handlers($chan) [list $patlist tchan $id $writer $thread]
- }
-
- return $id
- }
- remove {
- if {[llength $args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" }
- lassign $args rmid
- set newlist ""
- foreach handler [set chan.handlers($chan)] {
- lassign $handler _ type handlerid writer iot
- if {$handlerid != $rmid || $type ni {chan tchan}} {
- lappend newlist $handler
- } elseif {$type == "chan"} {
- puts $writer end
- flush $writer
- } elseif {$type == "tchan"} {
- puts $writer end
- flush $writer
- thread::release $iot
- }
- }
- set chan.handlers($chan) $newlist
- }
- default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" }
- }
- }
-
- # documented
- proc handler {subcommand chan args} {
- variable chan.handlers
- switch -- $subcommand {
- add {
- set thread false
- if {[lindex $args 0] == "-thread"} {
- set thread true
- set args [lrange $args 1 end]
- }
-
- if {[llength $args] ni {2 3}} { return -code error "wrong # args: should be \"irc::handlers add chan ?-thread? patlist script ?interp-or-thread?\"" }
- set iot [lassign $args patlist script]
- set id [format "%016x" [expr {round(rand() * (2**64))}]]
-
- if !$thread {
- if [llength $iot] {
- irc::int-setaliases {*}$iot
- interp share {} $chan {*}$iot
- }
-
- lappend chan.handlers($chan) [list $patlist script $id $script {*}$iot]
- } else {
- if ![llength $iot] {
- set iot [list [thread::create -preserved]]
- } else {
- thread::preserve {*}$iot
- }
-
- thread::send -async $iot [list set parent [thread::id]]
-
- lappend chan.handlers($chan) [list $patlist tscript $id $script {*}$iot]
- }
-
- return $id
- }
- remove {
- if {[llength args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" }
- lassign $args rmid
- set newlist ""
- foreach handler [set chan.handlers($chan)] {
- set iot [lassign $handler _ type handlerid _]
- if {$handlerid != $rmid || $type ni {script tscript}} {
- lappend newlist $handler
- } elseif {$type == "script" && [llength $iot]} {
- interp delete {*}$iot
- } elseif {$type == "tscript"} {
- thread::release {*}$iot
- }
- }
- set chan.handlers($chan) $newlist
- }
- default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" }
- }
- }
# nodoc
proc int-onextern {ichan chan} {
M src/main.tcl => src/main.tcl +18 -0
@@ 4,6 4,11 @@
# [manpage_begin main_thread tclircc 0.0.1]
# [titledesc {Thread main}]
# [description]
+# [para]
+# This is the application entrypoint. It does the following.
+# [list_begin itemized]
+
+
set path [file dirname [dict get [info frame 0] file]]
set version v0.0.1
@@ 21,6 26,9 @@ package require sqlite3
${log}::info "tclircc $version <https://amehut.dev/~aleteoryx/tclircc>"
${log}::info "running from $path"
+#***
+# [item]
+# brings up the routing system
src router.tcl
proc on_routes_update {} {
@@ 48,6 56,9 @@ proc start_thread {name} {
${log}::debug "started $name thread."
}
+#***
+# [item]
+# brings up the database
start_thread db
thread::send [r::ns tclircc::db] {path_to_core} core_db_path
@@ 56,11 67,17 @@ src migrate_core.tcl
src plugins.tcl
+#***
+# [item]
+# loads the other core threads
start_thread irc
start_thread ui
plugins::load [file join $path .. testplugin]
+#***
+# [item]
+# opens a window
${log}::debug "opening initial window..."
r::exec tclircc::ui {
mk_toplevel name
@@ 73,4 90,5 @@ ${log}::info "entering event loop"
vwait nil
#***
+# [list_end]
# [manpage_end]