{"id":1472,"date":"2008-12-15T11:38:19","date_gmt":"2008-12-15T14:38:19","guid":{"rendered":"http:\/\/brockerhoff.net\/bb\/viewtopic.php?p=2617"},"modified":"2010-05-08T12:17:42","modified_gmt":"2010-05-08T15:17:42","slug":"re-cocoa-musings-pt-1","status":"publish","type":"post","link":"https:\/\/brockerhoff.net\/blog\/2008\/12\/15\/re-cocoa-musings-pt-1\/","title":{"rendered":"Re: Cocoa musings pt.1"},"content":{"rendered":"<p>In my post on event taps, I mentioned the following code to get a global event tap:<\/p>\n<pre><code>\u00a0 \u00a0CFMachPortRef tapg = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGEventTapOptionDefault, \r\n\u00a0 \u00a0 \u00a0 CGEventMaskBit(kCGEventLeftMouseDown)|CGEventMaskBit(kCGEventLeftMouseUp),\r\n\u00a0 \u00a0 \u00a0 ProcessEvent,NULL);\r\n<\/code><\/pre>\n<p>This taps the event stream at the &#8220;annotated session&#8221; point (hence the kCGAnnotatedSessionEventTap parameter). Basically, this means that the event has already been analyzed as to destination application, and you can ask the passed event directly for that application&#8217;s process ID. This is was I was using in Klicko.<\/p>\n<p>Unfortunately the docs don&#8217;t mention another step that takes place while &#8220;annotating&#8221; a mouse-down event: apparently, if the click was on a window&#8217;s title bar (not elsewhere in the window), the owning process is brought to the front before the tap sees the event! Klicko&#8217;s processing varied depending on the clicked-on window&#8217;s and the owning process&#8217; state, so I was seeing different results depending on the click location &#8211; title bar or inside the window. Worse, a workaround I needed to do to bring non-main windows to the front for background processes wouldn&#8217;t be applied at all if the user clicked on a title bar!<\/p>\n<p>A more subtle consequence was that I was intercepting (and discarding) mouse clicks, while the system was expecting the click that brought the process to the front to actually arrive at the process&#8230; the result was that, while the menu bar changed to the process, its windows would remain in the back.<\/p>\n<p>After beating my head against this wall for several days, I was rereading the CGEvent docs again for ideas and noticed that I hadn&#8217;t tried applying a global tap before event annotation:<\/p>\n<pre><code>\u00a0 \u00a0CFMachPortRef tapg = CGEventTapCreate(kCGSessionEventTap, kCGEventTapOptionDefault, \r\n\u00a0 \u00a0 \u00a0 CGEventMaskBit(kCGEventLeftMouseDown)|CGEventMaskBit(kCGEventLeftMouseUp),\r\n\u00a0 \u00a0 \u00a0 ProcessEvent,NULL);\r\n<\/code><\/pre>\n<p>Note the kCGSessionEventTap parameter. This meant that I had to discover the owning application&#8217;s process ID myself (instead of asking the mouse-down event directly). Premature optimization strikes again; I&#8217;d selected the annotated tap just to avoid doing these steps.<\/p>\n<p>At any rate, I immediately discovered that I now could properly intercept clicks anywhere, discard them, and do process and window activation as I wanted to. Well, almost; there was still some redundant window activation to do, as Carbon and Cocoa apps seem to still have some residual differences in that regard. I&#8217;ll be filing bugs at Apple about some of these things.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my post on event taps, I mentioned the following code to get a global event tap: \u00a0 \u00a0CFMachPortRef tapg = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGEventTapOptionDefault, \u00a0 \u00a0 \u00a0 CGEventMaskBit(kCGEventLeftMouseDown)|CGEventMaskBit(kCGEventLeftMouseUp), \u00a0 \u00a0 \u00a0 ProcessEvent,NULL); This taps the event stream at the &#8220;annotated session&#8221; point (hence the kCGAnnotatedSessionEventTap parameter). Basically, this means that the event has already been analyzed [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4,19],"tags":[26,28],"class_list":["post-1472","post","type-post","status-publish","format-standard","hentry","category-dev","category-software","tag-cocoa","tag-klicko"],"featured_image_src":null,"author_info":{"display_name":"Rainer Brockerhoff","author_link":"https:\/\/brockerhoff.net\/blog\/author\/rbrockerhoff\/"},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1q3Zc-nK","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts\/1472","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/comments?post=1472"}],"version-history":[{"count":0,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts\/1472\/revisions"}],"wp:attachment":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/media?parent=1472"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/categories?post=1472"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/tags?post=1472"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}