|
|
|
@ -60,15 +60,16 @@ The other lines in this comment, that is, the lines that do _not_ begin with
|
|
|
|
|
project, that is just one line: the line that includes `procmon.h`, the header
|
|
|
|
|
file for the C code that we want to access.
|
|
|
|
|
|
|
|
|
|
Down [in the Go program's `main` function](procmon.go#L50), we spawn a goroutine to listen on a
|
|
|
|
|
channel for changes:
|
|
|
|
|
Down [in the Go program's `main` function](procmon.go#L50), we spawn a
|
|
|
|
|
goroutine to listen on a channel for changes:
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
go reportChanges()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `reportChanges` function simply reads values off of a channel and prints
|
|
|
|
|
them:
|
|
|
|
|
[The `reportChanges`
|
|
|
|
|
function](https://github.com/jordanorelli/procmon/blob/master/procmon.go#L38-L47)
|
|
|
|
|
simply reads values off of a channel and prints them:
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
func reportChanges() {
|
|
|
|
@ -106,12 +107,12 @@ void MonitorProcesses() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This function does two things: it starts by accessing a singleton of our
|
|
|
|
|
Objective-C class `ProcWatcher` (that's `[ProcWatcher shared]`, which is defined
|
|
|
|
|
[here](ProcWatcher.m#L6)) and
|
|
|
|
|
invoking its `startWatching` method. This subscribes our `ProcWatcher` instance
|
|
|
|
|
to OS notifications. We'll come back to how the ProcWatcher subscribes to
|
|
|
|
|
events in a bit.
|
|
|
|
|
This function does two things: it starts by accessing a singleton of [our
|
|
|
|
|
Objective-C class `ProcWatcher`](ProcWatcher.m#L4) (that's `[ProcWatcher
|
|
|
|
|
shared]`, which is defined [here](ProcWatcher.m#L6)) and invoking [its
|
|
|
|
|
`startWatching` method](ProcWatcher.m#L16). This subscribes our `ProcWatcher`
|
|
|
|
|
instance to OS notifications. We'll come back to how the ProcWatcher subscribes
|
|
|
|
|
to events in a bit.
|
|
|
|
|
|
|
|
|
|
#### sidebar: the Run Loop
|
|
|
|
|
|
|
|
|
@ -135,11 +136,9 @@ returns in the Go program, the Go runtime ends the process, which is _not_ what
|
|
|
|
|
we want. So this call gives us two things: it sets up the notification system
|
|
|
|
|
infrastructure, and it prevents our program from terminating.
|
|
|
|
|
|
|
|
|
|
#### back to observing NSNotifications
|
|
|
|
|
#### end sidebar: back to observing NSNotifications
|
|
|
|
|
|
|
|
|
|
[The `startWatching`
|
|
|
|
|
method](ProcWatcher.m#L6)
|
|
|
|
|
accesses the current OSX user's
|
|
|
|
|
[The `startWatching` method](ProcWatcher.m#L16) accesses the current OSX user's
|
|
|
|
|
[`NSWorkspace`](https://developer.apple.com/reference/appkit/nsworkspace). The
|
|
|
|
|
`NSWorkspace` handle allows us to hook into
|
|
|
|
|
[`NSNotificationCenter`](https://developer.apple.com/reference/foundation/nsnotificationcenter)
|
|
|
|
@ -148,7 +147,7 @@ subscribe to the `NSWorkspaceDidLaunchApplicationNotification` and
|
|
|
|
|
`NSWorkspaceDidTerminateApplicationNotification` notifications. Here's the
|
|
|
|
|
subscription to the `NSWorkspaceDidLaunchApplicationNotification` notification,
|
|
|
|
|
which is signaled by the operating system to inform an observer that an
|
|
|
|
|
application has been launched by the user:
|
|
|
|
|
application has been launched by the user:
|
|
|
|
|
|
|
|
|
|
```obj-c
|
|
|
|
|
void (^handleAppLaunch) (NSNotification*) = ^(NSNotification* note) {
|
|
|
|
@ -190,7 +189,7 @@ bridging code in C that can be imported by our own C code. This allows our own
|
|
|
|
|
C code to call back into the Go program and invoke Go functions. cgo will
|
|
|
|
|
silently generate this C header file behind the scenes. That C header file,
|
|
|
|
|
which is given the totally obvious and well-documented name `_cgo_export.h` is
|
|
|
|
|
generated when you run `go build` by cgo, used to help compile our C code, and
|
|
|
|
|
generated by cgo when you run `go build`, used to help compile our C code, and
|
|
|
|
|
then deleted. You won't notice it getting written and deleted because it goes
|
|
|
|
|
by so quickly, but it's there, and it's on disk when our C code gets compiled.
|
|
|
|
|
In order to access those definitions from our C code, our C code has to import
|
|
|
|
@ -229,7 +228,19 @@ typedef GoInt64 GoInt;
|
|
|
|
|
typedef long long GoInt64;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
That intermediate type is generated automatically by cgo in the intermediate
|
|
|
|
|
header file `_cgo_export.h` (before deleting it). If you want to look at this
|
|
|
|
|
file as a reference to see what is visible to your C code, you can manually
|
|
|
|
|
invoke cgo with `go tool cgo ...`, passing in the files to be processed by cgo.
|
|
|
|
|
You shouldn't need to do this in the normal case, but it can be useful for
|
|
|
|
|
debugging.
|
|
|
|
|
|
|
|
|
|
Anyway, calling that C function invokes the corresponding Go function
|
|
|
|
|
`AppStarted`, which writes a value onto a channel. That value is read off of
|
|
|
|
|
the channel by our `reportChanges` goroutine and used to print out the name of
|
|
|
|
|
the App that had been launched or terminated by the user.
|
|
|
|
|
|
|
|
|
|
Feel free to run the project on OSX. After running it, you should get no output
|
|
|
|
|
and your terminal should be under procmon's control. Open _another_ OSX app and
|
|
|
|
|
you should see a line like `started: com.apple.Notes` (if you start Notes.app,
|
|
|
|
|
for example). If you don't ... pull requests welcome ;)
|
|
|
|
|