Customizing MBP/M1

I finally parted with my 7-year old MacBook Air for a new WFH setup consisting of

  • Macbook Pro M1
  • 2 x 24″ wide monitors
  • USB-C hub

I added a 10-in-1 USB-C hub as my “dock” to expand my ports. Essentially I require a USB-A for my wireless keyboard set (7 year old also), RJ45 for occasional networking and extra display ports for the dual extended monitors.

My original intention is to just have “one port” connected to my MBP as am I used to the “docking” model, so all the other connectors are served through the hub including USB-C PD and the dual displays. However the hub does not support DisplayLink, so it can only mirror the output and I still have to have an extra HDMI cable connected directly to my MBP. Boo…

UnnaturalScrollWheels

The first obvious discomfort is that Apple decided that it is natural for the mouse wheel should roll in the same direction as the trackpad. Not that it’s wrong, but I usually use the trackpad with Mac but the keyboard and mouse for Windows so my scroll direction is messed up. System Preferences allows me to reverse the scroll, but any setting affects both the trackpad and mouse wheel simultaneously.

Luckily I’m usually not the only one with these problems, and UnnaturalScrollWheels solves this gracefully.

brew install --cask unnaturalscrollwheels

Karabiner Elements

Next are the modifier keys. In Mac-world we have Control, Option and Command, whereas Windows have Ctrl, WinKey and Alt. Even then the Control works in different ways where Copy is Cmd+C vs Ctrl-C. For me because I’ve been using both in home and work I’m able to “code-switch” on both keyboards instead of mapping on to the other.

By setting my Citrix Viewer preferences I was able to get close to the Windows keyboard mapping, but Alt is still on WinKey and the left WinKey is forced to the right WinKey. System Preferences allows me to remap modifiers by input device but I still could not use my left WinKey for my commonly-used keystrokes like Win-E, Win-R with one hand.

Citrix Viewer options

With Karabiner-Elements I was able to remap the right WinKey to the left and push the Alt back to where it was. Finally I can do Ctrl-Alt-Del with peace.

brew install --cask karabiner-elements
Karabiner configuration

I also noticed that Karabiner can remap mouse clicks, including the middle click on the scroll-wheel. However it did not detect my wheel-scroll, so it was not able to replace UnnaturalScrollWheels.

Bonus Hint: Ctrl-Space for autocomplete may be by default mapped to Spotlight Search or Input Source change (I use multiple input languages) so they may need to be disabled/remapped in System Preferences > Keyboard > Shortcuts.

Bonus Problem: Alt-Tab in Citrix activates app switcher in Mac instead of inside remote Windows. I can still switch with Win-Tab, which is not as bad as Right-WinKey. Karabiner has “complex modifications” that can import rules from the web that seems to support this but I suspect I’ll still need to edit the rules to target an input. Another adventure for another time.

DisplayPlacer

My new monitors were placed above my MBP, giving me a triple-screen (the MBP screen was too big to waste). I decided to have Citrix Viewer span across the dual monitor giving me a dual screen, while my Mac activities remain on the MBP screen.

Triple T

Several issues:

  1. Stretching Citrix Viewer across two displays

In MacOS mission control, “Displays have separate spaces”. With that enabled, a window can only appear in one of the screens. Sure, Citrix Viewer has an View option to “Use all Displays in Full Screen” but that replaces all 3 displays I have, instead of 2.

I happily disabled it…
  1. Multiple extended displays with same model

Every time I come back to my workstation (either after a break or the next morning), the monitors and MBP are in sleep mode (which is good). But when I log in again, the MacOS reconnects to the displays and often gets them mixed up. I have to go back to System Preferences to swap the two monitor position each time.

There seem to be no way to consistently force one to be recognized as either one. I tried swapping the HDMI connection or turning them on in sequence, it turns out wrong most of the time.

https://apple.stackexchange.com/questions/49913/is-it-possible-to-get-os-x-to-remember-my-screen-arrangement

After a few days I got really sick of it and decided to fix it. Luckily for displayplacer, I was able to use a command to restore the display layout. On top of that I was able to align my MBP to the real center as in Display Preferences my dragging was not so accurate. (This is relevant to #3)

brew tap jakehilborn/jakehilborn && brew install displayplacer
displayplacer list
displayplacer "id:7F4AE512-B46E-4BDD-B537-4A2915732ADD res:1512x982 scaling:on origin:(0,0) degree:0" "id:EFE602E0-11AF-4FA7-8091-5756E196A81E res:1920x1080 scaling:off origin:(-960,-1080) degree:0" "id:EFE602E0-11AF-4FA7-B26F-A0E49098A77D res:1920x1080 scaling:off origin:(960,-1080) degree:0"

3. Lost window positions and size

After waking the MBP and monitors, not only the display layouts were gone, the windows that were on the extend displays got thrown back to the primary monitor as well. I have to re-position and resize the window each time. So, what if I can script the window position and size back, as well as run the displayplacer together with a global shortcut key? Mac Automator to the rescue!

Automating displayplacer was straightforward, I used “Run Shell Script” task and pasted the output from displayplacer list , the only caveat was to specify the full path as I have brew-ed it. I try to brew where available so I can manage versions, and uninstall it without wondering if I can drag applications to the Bin or I need an uninstaller.

/opt/homebrew/bin/displayplacer "id:7F4AE512-B46E-4BDD-B537-4A2915732ADD res:1512x982 scaling:on origin:(0,0) degree:0" "id:EFE602E0-11AF-4FA7-8091-5756E196A81E res:1920x1080 scaling:off origin:(-960,-1080) degree:0" "id:EFE602E0-11AF-4FA7-B26F-A0E49098A77D res:1920x1080 scaling:off origin:(960,-1080) degree:0"

The window stuff was trickier. I felt AppleScript could do it, but the basic “tell application to set bounds” didn’t work. Ultimately what worked for me was to go through System Events, and have commands to set the position and size separately. Also I discovered “window 1” was the little floating menu at the top so my intended target is “window 2”.

on run {input, parameters}

  tell application "System Events" to tell application process "Citrix Viewer"
    set position of window 2 to {-960, -1080}
    set size of window 2 to {3840, 1080}
  end tell

  return input

end run

Still that wasn’t enough. When I try to set a global shortcut on it, it required permission on whatever app was in the foreground. It does not make sense nor practical to grant every app this access, so an extra workaround to extract the script was required.

do shell script "osascript -e 'tell application \"SetCitrixViewerBounds\" to activate'"

Finally, It works!

Remote Chrome DevTools

If, for whatever reason, you want to use Chrome to inspect a site, but the site tries to be smart and deactivates the functionality when it detects you activate DevTools, you can try to use “Remote debugger” to try to bypass it.

This is achieved by starting Chrome with a debugging port and connecting to it from another Chrome instance.

Step 1: Launch your 1st Chrome instance. This will be your debugger. (This step is needed if not attempting to launch the 2nd Chrome will collapse it to the first one.)

Step 2: Launch 2nd Chrome with debugging port (below for MacOS)

sudo /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

Step 3: In the 2nd instance, navigate to the target site to be debugged.

Step 4: From the 1st instance, navigate to http://localhost:9222/ and you should see a list of “Inspectable pages”. Click on your target site.

DevTools should open, and you can inspect the site.

HACK: Change issue creator in Bitnami Redmine

I own the administrator account of a Bitnami Redmine that I installed, but I usually work using a regular user account (Unix rule of not using root). Unfortunately I made the unforgivable mistake of creating a regular issue using the Admin account. For “correctness” sake I tried, and searched if I could modify the creator… (talk about non-repudiation…)

Nope, no default method, or requires a plugin. I don’t intend to do this regularly, so I don’t really need a plugin. I decided to mess with the database directly and see if it was easy to understand the schema. Turns out it was too straightforward.

Notes:

  1. The mysql root password is the same password as the Redmine admin.
  2. I am using a Bitnami Redmine 3.1.0-0 instance, you may need to use “SHOW DATABASES;” to figure out which database.
  3. In the process I used “SHOW TABLES;” and “DESC issues;” to probe the schema. I am just showing the final necessary commands to run.
  4. You can get the issue ID by looking at the URL when the issue is displayed in your browser.
  5. You can mouseover the desired user in the browser to peek at the user’s ID to be used as the author_id.
> ./mysql -u root -p
Enter password: 
mysql> USE bitnami_redmine;
Database changed

mysql> UPDATE issues SET author_id=3 WHERE id=59;

Refresh your browser.

multiple definition of `R_running_as_main_program`

I inherited a C++ application that was using rcpp to embed R. After R was upgraded to 3.2, the make was failing miserably with the error:

file1.o:(.bss+0x0): multiple definition of `R_running_as_main_program'
file2.o:(.bss+0x0): first defined here

This was found to be caused by https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=15899

To overcome this, edit /usr/share/R/include/Rinterface.h and search for this line:

int R_running_as_main_program;

Add the “extern” keyword at the start and save it:

extern int R_running_as_main_program;

After that there should be no problem building the program.

Lenovo T440p keyboard

I was just issued a Lenovo ThinkPad T440p, not that I get to choose the model. Immediately I got down to customizing the quirks (to me).

1. Fn/Ctrl key swap
Ctrl-C Ctrl-V Ctrl-Z Ctrl-W all don’t work. Because in the Ctrl’s position is a Fn. This link explains why (so we could find the Fn key for ThinkLight in the dark), but I didn’t really need the ThinkLight so I swapped it anyway.

Go to BIOS > Keyboard/Mouse > Fn/Ctrl Swap > Enabled.

2. F1-F12 lock
F1-F12 could only be accessed with the Fn key. so F2 to rename became Fn-F2, and F5 to refresh became Fn-F5.

Press Fn-Esc to lock the Function keys.

3. Trackpad Scroll
I use a MBA at home so the two-finger scroll was reversed.

To keep myself sane, go to Control Panel > Mouse > Change Mouse Settings > ThinkPad > Advanced > Scroll > Two-Finger Scrolling > Check Switch Direction.

4. Lenovo Message Center Plus
The big red icon on the taskbar was an eyesore to me.

Right click on the taskbar > Toolbars > Uncheck Lenovo Solution Center.

Glassfish timezone different from OS (Ubuntu)

We added a new Ubuntu server to deploy one of our new feature to isolate it from the core modules. The new module did not work, and we narrowed it to System.currentTimeMillis() returning a time in the future.

NTP was active; we ran the linux “date” command and it was showing the correct date. When we checked Glassfish’s JVM report, it showed that the user.timezone was different, and the timezone difference coincides with the time differences we observed. The straightforward answer: set -Duser.timezone in the JVM options and we are good to go.

But wait, why our original servers didn’t have this issue? We checked the original server, and there was no such JVM option setting. After some tracing it turns out that /etc/timezone of the new server was incorrect, and that influenced the timezone Glassfish used. Finally we matched the two server settings and reverted the user.timezone JVM option.

mysql ibdata keeps growing

Didn’t manage to collect complete information for this post, so I’ll just write whatever I have.

It all began when our development database server ran out of disk space and crashed. We mounted a temporary virtual hard disk and moved the database there, and all was well for a while. With df and du we narrowed our culprit to mysql’s ibdata, which was growing so fast that we will run out of disk space again soon enough. Public information tells us ibdata is supposed to always grow, but we do not expect our dev ibdata to grow at this rate. After multiple searches this blog finally closes us in.

SHOW ENGINE INNODB STATUS

With the command, the innodb history list length was a very large number (>1mil) and keeps going up. A check with our test and production databases show that the length goes at most to a few hundred and drops back down — a significant difference. Restarting the database doesn’t help.

SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX

This showed us we had 16 XA transactions, that were started 2 days ago, but never committed or rolled back. They are not locked, so their trx_mysql_thread_id is 0. We immediately linked the causes together. Stuck XA transactions -> history list growing.

XA RECOVER

According to mysql docs, this command can rollback the XA transactions. The user comment was especially helpful on how to reproduce the xid, reproduced verbatim here:


To rollback the transaction, first get its xid:

mysql> xa recover;

+----------+--------------+--------------+------------------------------------------------------------+
| formatID | gtrid_length | bqual_length | data                                                       |
+----------+--------------+--------------+------------------------------------------------------------+
|   131075 |           30 |           28 | 1-a00640d:c09d:4ac454ef:b284c0a00640d:c09d:4ac454ef:b284c2 |
+----------+--------------+--------------+------------------------------------------------------------+
1 row in set (2.13 sec)

The xid is present in this output, but you have to perform a little string manipulation to get it. The format of a xid is: gtrid,bqual,formatID. The column 'data' contains a concatenation of 'gtrid' and 'bqual'. The columns 'gtrid_length' and 'bqual_length' specify how many bytes each of these values uses; use them to split apart 'data'. In this example, the result is:

mysql> xa rollback '1-a00640d:c09d:4ac454ef:b284c0','a00640d:c09d:4ac454ef:b284c2',131075;

ERROR 1402 (XA100): XA_RBROLLBACK: Transaction branch was rolled back

The tricky part here was that, my data had binary characters, which I couldn’t directly copy and paste in the MySQL Workbench. I couldn’t bear to write a program to read the value and write it back either, so I was poking around for solutions on that. From the same mysql doc page,

gtrid and bqual must be string literals, each up to 64 bytes (not characters) long. gtrid and bqual can be specified in several ways. You can use a quoted string ('ab'), hex string (0x6162, X'ab'), or bit value (b'nnnn').

Good, I could write the xid in hex. So I right-clicked the data column, and “Open Value in Viewer”. In the binary tab I copied down the hex values and reconstructed the xid as described by the helpful comment.

XA ROLLBACK X'7e3ae860eb21de21b84d392cb03bf8363b41482b9b1207f6e6823355012e91858c',X'526801e7500b06fd05a2f5882d20be19982a46aed4b6c26dc63887',4264851;

Viola, one by one the transactions were gone. Once the last one was rolled back, the history list started to decrease and behave in a similar pattern as our other databases, and the ibdata stopped growing at the crazy rate. The one last part I haven’t figured out is: how do I copy the hex values from MySQL Workbench, or how do I show the XA RECOVER data column in hex?

Firefox marquee restarts prematurely

I was trying to fix a HTML marquee bug today, where only on Firefox the marquee restarts itself before the whole message scrolls to the end. After stripping down the page the cause appears to be Firefox observing the length of the original text (which was just a single space character).

The HTML code below reproduces it. Click on “Change Marquee Text” to see the difference between the two marquees.




At first


Later, the marquee text became longer but Firefox does not recognize it.

Not worth to further investigate here, I just dynamically re-rendered a container div for this marquee.

Change SVN commit message

This day finally came when I copied/paste the wrong bug number into the SVN commit message. Usually errors in SVN commit messages can be ignored but this particular one may lead to much confusion later. Luckily there was a way to edit the commit message after it’s committed. As usual I paste the command here instead of just the reference link as I have already some posts that have dead reference links.


svn propset -r 12345 --revprop svn:log "#1234 description"

where 12345 is the revision number, and text in quotes is the corrected commit message.

I still prefer to be cautious to commit messages correctly, but humans are just humans…

Ref: http://subversion.apache.org/faq.html#change-log-msg

Netbeans Subversion 1.6 vs 1.7

I installed the latest and greatest TortoiseSVN on a new PC (which happen to be for SVN 1.7), and happily upgraded my working copy when prompted. Newer is better isn’t it? After all, I didn’t like the .svn folder everywhere, which 1.7 got rid of with centralized metadata storage.

To my horror later, Netbeans wouldn’t update my workspace anymore, since the current version has no way to support 1.7 yet, other than the command line client. Fine, I installed the 1.7 CLI over my 1.6 CLI, but the Subversion features in Netbeans became sluggish and often show incorrect change flags until I manually activated Show Changes each time.

Downgrading to 1.6 was not an easy feat, I installed TortoiseSVN 1.6.16, judging by it being the latest release bound to SVN 1.6. I was lucky my SVN server is local so re-checking out my workspace was not that tough (I know some places which will take hours, and I haven’t found a “downgrade working copy” method that didn’t require a checkout).

Then Netbeans complained the working copy isn’t compatible anymore with my SVN 1.7 CLI. I cleared the SVN path setting, but couldn’t find any option to force it to revert to using the built-in bindings. Reinstalling the plugin did not help either. A half hour later I resorted to a brute force search on “subversion” in the netbeans folder and my home folder and I finally found this file:


C:\Users\%USERNAME%\.netbeans\7.1\config\Preferences\org\netbeans\modules\subversion.properties

where the option “forcedCommandline” is set to true. Changing it to false and restarting my IDE got me out of the situation and ended my fight with SVN 1.7. I still like the idea of the new working copy, so hopefully a new NB SVN plugin will be released soon.