Making Windows keyboard programming tools work for macOS

On of the first things I noticed, when I started to get into the mechanical keyboard hobby, was the lack of support for macOS. Most of the more “mainstream” mechanical keyboard manufacturers are still using proprietary programming software that only runs on Windows. This poses a problem for someone who typically uses macOS. Lucky for me, I still own machines that run Windows. However, even this doesn’t help when it comes to the proprietary weirdness that is Apple.

I use the same key combo a few times per day, CTRL + SHIFT + EJECT. In macOS, this shutoff the display and locks the computer. Any time I get up from my computer, I just lock it with those three keys out of habit. Sure, the odds of someone trying to get access to my work computer without someone noticing in a small office of less than 10 people is pretty slim, but I just consider it a good security habit at this point.

That key combo presents an issue. The eject button on an Apple keyboard does not actually send a key code. This wasn’t a problem when I was using QMK, but it does present a problem when I’m limited to these proprietary programming tools that most companies offer. How can I map a key to a key code that doesn’t exactly exist? I thought I would flex my intelligence and use QMK to solve the problem. I decided I’d use my KBParadise V60 Type R to send the KC_POWER key code to the Windows programming tool. It only seemed logical as KC_POWER was the solution to locking macOS using a QMK macro.

… Except it didn’t work. It seems that the Drevo programming software was only designed to accept a predefined range of key codes, and KC_POWER was not within that range. Back to the drawing board. Maybe I could send KC_EJCT? Maybe there was another key combo that could accomplish the same task?

Well… several failed ideas/attempts later I discovered that CTRL + CMD + Q would lock the computer, but it would not shutoff the display. I could then use ESCAPE to shutoff the display. While this was now a two-step process, it was not a process that only used key codes recognized by Windows and Drevo’s software. I managed to record a macro that would send CTRL + CMD + Q followed by ESCAPE a few milliseconds later, and it worked. The solution was more convoluted that I would have hoped for, but I managed to get this Drevo Calibur to recreate the result of a key combo that the manufacturer’s software was not capable of recognizing.

* pats self on back *

In the world of mechanical keyboards the phrase, “where there’s a will, there’s a way,” holds true. Sometimes you need to think outside the box and explore counterintuitive options, but the desired result is almost always achievable with a bit of persistence.

Exploring the world of clacking keys

I always seem to be exploring a new hobby. Whether it digital/electronic or more offline, I love learning new things. My latest adventure has brought me to an intersection of my enjoyment of building physical things and programming. I’ve been lightly interested in exploring mechanical keyboards for a few years, occasionally glancing into the r/MechanicalKeyboards sub-Reddit, but I finally decided to pull the trigger and buy one a little over a week ago. I did some research on all kinds of boards, but I ultimately decide to just buy a cheap one to give it a try first.

ACGAM AG6X

It seemed like Amazon was taking forever to deliver it. (Mainly because I’m impatient.) But my new ACGAM AG6X finally arrived. It’s a fairly solidly built board for it’s reasonable ~$45 price tag. Nothing super fancy. Plain white backlighting, only one choice of switch type, and plain black keys. I had a few hiccups that are mainly related to my use of macOS, which isn’t widely supported by the companion software provided with most boards. I highly suggest at least having access to a Windows machine, as the companion software allows you to remap the keys. The main downside to not using Windows is that the macros you set in the companion software are not saved to the board, thus they only work on Windows. As side from the OS related issues, it’s a great board for the price point. I’m still surprised how solid the construction feels. It was enough to make me want more. I really wanted to explore the world of macros. After some more research, I decided that I wanted a board (or keeb as the cool kids call them) that would run the QMK firmware.

KBParadise V60 Type R Polestar
( Turns out I have a habit of photographing keyboards from the same angle. )

I headed over to MechanicalKeyboards.com to order my new board. I decided on a KBParadise V60 Type R Polestar… yes, it’s a mouthful. I have jokingly called it the keyboard with all the names. It’s been great so far. It’s a bit fancier than the AG6X, as the V60 has RGB LED underglow lighting. I’ve also had fun poking around at QMK and setting up custom layouts for my board. Plus, I’ve gotten to explore macros. I’ve set up macros for everything from a full HTML boilerplate to ASCII art. It’s really interesting to have this base level of customization just built into the board automatically no matter which device I connect it to, as all the info is store on the keyboard. Need to whip up a quick HTML page on the go? Just plug it into whichever device is closest (even an iPad or iPhone), and in just a couple keystrokes, I can output a full HTML boilerplate. Need a basic jQuery onDomReady statement? Just another keystroke. Maybe I need a ROFL Copter? Yep, I can program that in too. The customization possibilities are virtually endless.

My awesome little monster, from Shapeways, hanging out on my escape key.

And speaking of customization, you can really make these keyboards your own. With tons of different color options for keycap sets plus artisan keycaps, your board can really be a reflection of you personality. As my other half put it, “it basically a charm bracelet for geeks.” I’ve picked up a couple cool 3D printed caps from Shapeways, and I’ve got a few Artisan made caps on the way too.

I’m sure I’ll be sharing more, as I dive into this ever growing industry of “keebs”. With everything from pre-built boards to hand wired custom made boards, it seems like it’s a hobby you can get really deep in. I’ll leave you with a glamor shot of one of my new caps that’s on its way.

Phantom from KESPNkeycaps

Giving Windows another shot

Microsoft has been putting out what seem to be some really excellent products after the past couple years. The Surface Pro 3, SP4, Surface Book, and Surface Studio looked to be extremely polish compared to the average Windows machines, but I haven’t really touched a Windows machine since Windows 7.

Let’s go back to 2014, I was working for Apple as an AppleCare Advisor for iOS devices, and it turns out, plenty of people use their iOS device with Windows. I had zero knowledge of Windows 8, but I wanted to make sure I was as confident in navigating Windows 8 as I was Windows 7. This lead me to pick up a Dell Venue 8 Pro. Beyond using it to learn Windows 8 settings, charms bar, and control panel navigation, it saw some occasional use as a couch web surfing device. I was kind of a fan of Windows 8, but it really only excelled on a tablet. I was engulfed into the Apple ecosystem (I still am), but from where I was standing, it didn’t appear anyone was putting out high quality Windows devices.

Fast forward to 2017, Microsoft is putting out some killer devices. Sure.. Windows phones kind of flopped, but the Surface line seems really polished. I still enjoy my Apple products, but I want to take a peek into the Windows world again. Coming off of CodeMash 2017, I decided I’d start looking. Why? This year CodeMash had a CTF run by Hacking Lab, and I decided I’d see what it was all about. Step 1, install VirtualBox and boot the supplied VM… Turns out an Apple MacBook, with it’s Core M processor, doesn’t like to run VMs. Leaving CodeMash this year, I felt that I wanted to peek into the Security world a bit more and would need a bit more horse power. Yes, I have a desktop… and sure, I could by a MacBook Pro. But I really like my small form factor portables. I started looking at 12″-14″ options for Windows Machine. Looked at a ThinkPad(In my life before Apple products, I always wanted a ThinkPad), Surface Pro 3, Surface Pro 4, and a couple others that were eliminated fairly quickly. I wanted to keep it under $700, since I wasn’t entirely sure I would be able to use Windows everyday. I tried a few devices, but I ended up picking up a loaded used Surface Pro 3(Core i7, 8GB, 512GB SSD).

How is it? … I kind of like it. I miss the astonishingly great battery life Apple always manages to squeeze out of the devices, but I know I could fix that with a Surface Book. This is the device I wish Apple would build. Do I use the pen? Not too often. I’m sure I will more in time as I find places for it. I don’t even use the touchscreen very much, but I think it’s the option to use them if I wanted that I like. The keyboard is pretty good, but I’m not much of a keyboard aficionado. Part of what I like about it is the form factor. It’s is a tablet instead of a laptop. I know that it doesn’t seem like a big deal, but I think it’s easier to turn a tablet into a good laptop than it is turning a laptop into a good tablet. I really think that Apple could get here. They really are not that far off. If they add a navigable file system and cursor support to the iPad, they would be 95% of the way here. The rest is dependent on developers delivering killer apps.

The biggest hang up I’ve found using the Surface is undoubtedly the lack of affordable web editing software with decent UI design. I think I’ve looked at a minimum of 10 different programs to replace Espresso by MacRabbit. It didn’t seem like it would be hard to replace, but I’ll cover that search in my next post. I also miss AirDrop. How do Windows users survive without AirDrop?! Yes, there are thousands of ways to transfer photos, files, etc. from my phone to a computer, but AirDrop is sooo easy. Beyond the lack of a good web editing program and AirDrop, I haven’t found too much I miss yet. In some ways, I’m even embracing the Windows ecosystem. Microsoft Edge has been treating me fine. It’s going to remain my daily driver, until it presents a reason to install Chrome. I know many are partial to Chrome’s Dev Tools, but Edge’s F12 Dev Tools are perfectly fine so far.

All in all, I’m really digging this Surface. I would even go as far to say that the Surface Book has moved into consideration for replacing my MacBook. If Microsoft keeps up it’s current pace, I’m even looking in to the possibility of replacing the iMac at my day job with a Surface Studio in a year or two. They are putting out some solid hardware that is making some Apple fans a bit envious, and as an Apple fan, we can only hope that Microsoft’s devices will nudge Apple into responding with competitive and innovative devices.

A bit dusty… The TL;DR of the past 1.5 years.

As you can tell, I haven’t posted in over a year and a half. I started this blog back up a few months before attending my first CodeMash. It was a week of exhaustion and immersion that kind of changed me for the better. ZAGG Studios, Ltd. (The company I own with Missy) photographed the event. We knew it’d be a bit crazy, but I think it surpassed our expectations. CodeMash is amazing. They have 4 days of developer talks ranging across a wide range of technologies and languages. I really feel that there is something for everyone at CodeMash, but that is also the problem from the photography side. There are sooooo many sessions… It’s kind of crazy. About halfway through, I wasn’t sure I’d make it. I had these visions of Missy having to drag me out after I collapsed, but I was having the time of my life. We met tons of people from all kinds of backgrounds. I got to sit in on a few sessions. It helped me with some small struggles I’ve had with impostor syndrome. It was this crazy blur of a week, but we signed back on to shoot CodeMash 10. In between CodeMash v2.0.1.5 and CodeMash 10, I focused on contributing to StackOverflow, completed my first year with the day job I took after leaving my iOS support position at Apple, and we found a new favorite vacation spot. CodeMash 10 rolled around. We trimmed back the number of sessions we shot in order to focus on quality and health/well-being. Ha ha. CodeMash 2015 really was a bit crazy for us. This time around, we had a year of experience under our belts, friendships from the year prior, and nicknames. CodeMash 10 was comfortable. We made it through with relative ease and even did some testing with video for some key events. If you want to poke around the pictures, you can look at them here. Other than my developer/photog life, I bought a new motorcycle this year which I’m sure will be featured on here more. I think this blog is going to transform once again as I include more of my daily thoughts and photography (especially 360 photography). I also hope to get back into the habit of posting regularly. Stay tuned!

iOS Exit Offers

I was recently tasked with developing a exit script to offer a coupon code before a customer leave the site. I managed to create a cross platform exit offer script that is almost done*. I think the most interesting part of my solution is how I am displaying this offer for iOS device. It needed to be easy. Desktops, notebooks, tablets, etc. (any device that runs flash) can utilize ZeroClipboard to copy text to the clipboard in just one click. iOS devices do not have flash, so ZeroClipboard isn’t an option. I decided not to pour hours into creating a one tap “copy” to clipboard for iOS, since most people using their iOS device for purchasing goods are probably fairly savvy. I am more concerned on prompting these users at the best time without the prompts becoming overly annoying.

I started with some criteria:

  • Simple for savvy users
  • Do not prompt on a refresh
  • Only prompt once the user has visited the same page twice
  • Prompt should not fire on internal navigation

And this is what I ended up with: ios-exit.js
*Nothing is ever complete. There always seems to be something I can tweak to make it a bit better.The JS:

//iOS Exit Offers
var exitmsg = "Shhh! Don\'t tell anyone, but we\'ll give you a coupon .\n\nCopy your coupon code from the space below.\n\nCoupon Code:";
var exitcode = "theCouponCode";
var siteurl = "techsock.com"; //No http:// needed

var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);

function setOne() {
  document.cookie = "internal=one;domain=97.74.4.64;path=/";
  console.log('oned');
}

//Change internal cookie to "one" on internal navigation
function internalLinks() {
  var elements = document.getElementsByTagName('a');
  for (var i = 0, len = elements.length; i < len; i++) {
    elements[i].setAttribute("onMouseDown", "setOne();");
  }
}
internalLinks(); //Add OnClick Event to all Internal Links

function getCookie(c_name) {
  if (document.cookie.length > 0) {
    c_start = document.cookie.indexOf(c_name + "=");
    if (c_start != -1) {
      c_start = c_start + c_name.length + 1;
      c_end = document.cookie.indexOf(";", c_start);
      if (c_end == -1) c_end = document.cookie.length;
      return unescape(document.cookie.substring(c_start, c_end));
    }
  }
  return "";
}

function setCookie(c_name, value, expiredays) {
  var exdate = new Date();
  exdate.setDate(exdate.getDate() + expiredays);
  document.cookie = c_name + "=" + escape(value) +
    ((expiredays == null) ? "" : ";path=/;expires=" + exdate.toUTCString());
}

function exitOffer() {
  //Show Coupon Code in Text Field within Prompt for iOS Users
  if (iOS === true) {
    //Check to see if user has been shown coupon before
    couponcode = getCookie('couponcode');
    if (couponcode != null && couponcode !== "") {

    } else {
      coup_timer = window.setTimeout(function() {
        var conf = prompt(exitmsg, exitcode);
        if (conf) {
          //User tapped OK
          setCookie('couponcode', 'prompt-yes', 3);
          //Might utilize these cookies at a later date
        } else {
          //User tapped cancel
          setCookie('couponcode', 'prompt-no', 1);
          //Might utilize these cookies at a later date
        }
      }, 700);
    }
  }
}

function checkInternal() {
  //Check to see if internal link was followed
  internal = getCookie('internal');
  if (internal !== null && internal !== "") {
    if (internal == "zero") {
      exitOffer();
    } else {
      window.setTimeout(function() {
        document.cookie = "internal=zero;domain=" + siteurl + ";path=/";
      }, 500);
      console.log('zeroed');
    }
  } else {
    window.setTimeout(function() {
      document.cookie = "internal=zero;domain=" + siteurl + ";path=/";
    }, 500);
    console.log('zeroed');
  }
}

var title = document.getElementsByTagName("title")[0].innerHTML;
var nospacetitle = title.replace(/\W/g, '');
var cleantitle = nospacetitle.replace(/ /g, '');

function checkCookie() {
  //Fire Exit Offer on Second Time a Page Loads
  pagename = getCookie(cleantitle);
  if (pagename !== null && pagename !== "") {
    lastpage = getCookie('lastpage');
    if (lastpage !== null && lastpage !== "" && lastpage == cleantitle) {
      console.log('samepage-refreshed');
      setCookie('lastpage', cleantitle, 1);
    } else {
      console.log('navigated-from-dif');
      setCookie('lastpage', cleantitle, 1);
      checkInternal();
    }
  } else {
    console.log('no-page-cookies-yet');
    setCookie(cleantitle, 'pagename', 30);
    setCookie('lastpage', cleantitle, 1);
  }
}
checkCookie();

//Force iOS to run exit offers on backward navigation
if (iOS === true) {
  window.onpageshow = function(evt) {
    //If persisted then it is in the page cache, call JS function again.
    if (evt.persisted) {
      checkCookie();
    }
  };
}

Net Neutrality…

We all love buzz words, but the reality is most people don’t really understand what is being discussed. As a citizen of the internet, I am very appreciative that Leo Leporte & the TWiT gang took time to put together a debate on the subject with two independent ISPs. I’m a firm believer that there is no such thing as too much information, and whether we realize it or not, this will be a definitive moment in the history of our generation. Check it out over at TWiT. (P.S. – The debate is only in the beginning, but feel free to continue watching. The panel also discusses “Dirtboxes” That’s a topic for another post.)

Replacing Nibbleblog’s Image Upload

Recently, I’ve having some issues with the image uploader built into Nibbleblog v4.0.3. I managed to track them down, but I haven’t found a solution yet. However, Nibbleblog v4.0.3 uses TinyMCE for editing posts and pages. Using that information, I set out to find a solution. I found JustBoil.me Images. JustBoil.me is a TinyMCE plugin image uploader that is fairly simple to set up. I figured I’d write a quick how-to for anyone else using Nibbleblog and having issues with the built-in image uploader.

Note: Nibbleblog v4.0.3 uses Version 4.0.20 of TinyMCE, if you are using a different version of Nibbleblog or TinyMCE you may need to modify these instructions.

Step 1: Pop over to JustBoil.me and click the “Download @ Github” button. Once your on Github, you’ll want to find the “Download ZIP” button on the right side of the page.

Step 2: Now that you have the zip downloaded, we can either unzip the file or upload on to the server and then unzip the file. We want to place the file or folder into the admin/js/tinymce/plugins directory of your Nibbleblog installation. *Optional: Change the name of the JustBoil.me folder to “jbimages”.

Step 3: Once you have JustBoil.me placed into your admin/js/tinymce/plugins directory, open config.php inside of the JustBoil.me folder. You’ll want to make a change to line 41. Line 41 set’s the directory to which JustBoil.me will upload the images. I set mine to /content/public/upload. This matches the default upload location of Nibbleblog’s default image uploader.

Step 4: Go ahead and open up content.bit located in the admin/views/post/includes/ directory. We need to make a few changes here as well. We need to add “jbimages” somewhere into the list on plugins that starts on line 35. We also need to add “jbimages” to the list of toolbar buttons that starts on line 40. *Optional: If you no longer wish to use the default image uploader, delete “nb-image” from the toolbar section and comment out or delete line 72 through line 80.

Now login to your Nibbleblog and create or edit a post. You should now see your shiny new image uploader in your TinyMCE toolbar. If you have any issues or questions, drop me a line in the comments.