MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Wednesday, October 09, 2024

From the Nikkor - The Thousand and One Nights Collection's 13th night (ostensibly about the <New> Reflex-Nikkor 500mm F8, which I may have recently purchased on eBay) at Nikon.com:

We used to call Mr. TSUNASHIMA with affinity and respect "Boss, TSUNASHIMA". Certainly, he was a big-brother type person and took thought for younger men. His dynamic manner did not limit to work-related things. Mr. TSUNASHIMA, who would play tennis for years, was a hard drinker equal to Mr. MORI, introduced in Tale Nine. Let me introduce you an episode of "Boss, TSUNASHIMA" at a drinking party.

It was about my freshman's time. At a drinking party on a company trip, several people (hard drinkers) of us were sitting in a circle and drinking sake (Japanese rice wine). Sake bottles were rapidly emptied one after another. The party really came alive and people's laughing voice and cheers echoed. Finally, all the sake bottles were emptied. So, Boss, TSUNASHIMA gave a cry to me, one of organizers, "Hey, SATO! Sake is empty." So I said, "Yes, sir," and brought several bottles of beer. Then, Boss, TSUNASHIMA exclaimed, "Hey, SATO! Is this really sake ?" I replied, "Well ?" Boss, TSUNASHIMA suggested, "Well, this is an alcoholic beverage called BEER, isn't it ?" So I knew that after all, a heavy drinker was different. The scales dropped from my eyes. I realized that I could not join in the party unless understanding their delicate manner and feelings.

That's, um, unexpected.


I've always thought reflex lenses were interesting, but always read about their poor image quality and small aperture, meaning you couldn't take very quick pictures in low light, so I never really gave them a serious thought.

But after buying a used, manual focus 300mm f/4.5 lens years ago and really enjoying using it on my D40, I've been a little less adverse about putting really old lenses on really new cameras.

I've got a few pictures on Wikipedia that I took during NFL games years ago, and kinda missed having my camera with me when I went to a game last week. But the rules have changed, and my old 80-200mm technically shouldn't be allowed in any more, since it's over the new rule of a max length of 5" on detachable lenses.

You can probably see where this is going. How can I get an ultratelephoto lens into an NFL game? Well, you get a 500mm reflex mirror lens that's 109mm long (so 4.3") to a bright arena without much in the way of shadows, and see what you've got.

We'll see how good of a specimen I bought when it arrives, and I'm a little worried about how narrow the depth of field is, but that does seem to go with the wide-open telephoto territory. I think the extra ISO digital allows will more than make up for the loss of one stop of maximum aperture.

Will be fun to give it a shot in any event. Don't know that I'll have much to report about sake or BEER, however, other than my absolute horror at how many $18 cans people around me seem to be downing. Seriously, dropping $50 to buy your best friends a round seems, um, a little steep.

And a little more context on why Mr. Tsunashima factored into what amounts to a blog on the history of Nikon's reflex lenses:

The optical system was designed by Mr. TSUNASHIMA, Teruyoshi of 1st Optical Section, Optical Designing Department (then). Mr. TSUNASHIMA ranked with Mr. MORI, Ikuo, introduced in Tale Nine, and Mr. SHIMIZU, Yoshiyuki, introduced in Tale Five, was one of the designers who built up the golden age of old Nikkor lenses.

He completed optical design of Reflex-Nikkor 500mm f/8 (New) in August 1982 and, later, obtained patent right both in U. S. and Japan.

Waiting until the time was ripe, the lens was on sale in spring of 1984, when trees put out leaves. The specifications of the lens, being light and compact and having amazingly short closest focusing distance of 1.5m, got publicity and accelerated the Reflex-lens boom of those days.

...

In other words, Mr. TSUNASHIMA designed the Reflex-lens in person, struggled to produce it in large quantities, and strictly controlled its quality by himself to put it on sale. Consequently, Mr. TSUNASHIMA participated in the own designed lens up to a stage just before shipment, which was the nearest stage to users.

Labels: , , ,


posted by ruffin at 10/09/2024 09:18:00 PM
Tuesday, October 01, 2024

Okay, I've had this open in drafts too long. I think it's got most of the info I wanted, so let's cut it loose for when I need it in the future.


I often take a different laptop with me when I'm travelling than whatever the "prime" development box is for a project, often to ensure I don't lose sensitive information if the laptop "disappears" while I'm out. When doing this, I usually copy the folder I'm working in, remotes (so personal access tokens, VPN setup, etc) be darned, and work from that.

The issue is often getting that work back onto the "prime" boxen. That usually means remembering how to make and apply git patches.

Look, here's the deal... ;)

If you want to copy over and preserve individual commits, you want to use "email" formatted patches. You can envision why. If you came before the time when everyone had shared remotes or if your workforce is distributed and most simply don't have remote access, it's easy to schlep around code via email. And so git has email support built-int! Though do note we're only using the format, as it carefully preserves each commit separately; we're not actually emailing anything. Unless you really want to.

On the travelling box:

Let's say I wanted the last 5 commits. I'd use this command to create an email-formatted patch file:

git format-patch -k --stdout HEAD~5 > patch.patch

Open up the text file and take a look! It's actually kinda interesting, begging for an SMTP server to send it on its way.

On the "prime" development box:

git am -3 patch.patch

Now look, if you used git apply here, it would apply EVERYTHING IN THE FILE AS A SINGLE ACTION and not commit anything. It's like rolling all the changes into a single worksession that needs to be committed. Using git apply for an email versioned patch reduces to the same operation as creating a diff with git diff and git applying it.

We DON'T want that. You have to use git am to get the email action going.

The -3 is for three-way merge if there's a conflict git can't resolve, and is the way I (and several other StackOverflow users, apparently) best prefer to manage conflicting patch applications. But you really shouldn't run into that much if you just worked on an existing branch.

Do make sure you're on the right branches on both boxes.


TODO: How do you get the patch to include staged files?

Labels: ,


posted by Jalindrine at 10/01/2024 11:21:00 AM
Friday, August 23, 2024

Okay, look, if there's one thing I'm tired of, it's half-baked example code that doesn't anticipate changes needed to push it into production.

Like the good ole WeatherForecastController from the .NET Core WebAPI template.

using Microsoft.AspNetCore.Mvc;

namespace MyApp .Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", 
            "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

I mean, Visual Studio immediately complains:

Remove this unread private field '_logger' or refactor the code to use its value.

Well, duh. We have an endpoint with no logging. When would we need to log? Probably when we're doing something more complicated than creating random 8-ball style forecasts.

So let's pretend it's more difficult, throw in a try... catch, and actually log the exception.

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
    try
    {
        return new ActionResult<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }));
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Something failed");
        return BadRequest("that didn't work");
    }
}

Guess what? Now we got TWO errors! YAY!!

  1. CS0029 Cannot implicitly convert type 'Microsoft.AspNetCore.Mvc.ActionResult<System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>>' to 'System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>'
  2. CS0266 Cannot implicitly convert type 'Microsoft.AspNetCore.Mvc.BadRequestObjectResult' to 'System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>'. An explicit conversion exists (are you missing a cast?)

Dare you to tell me what to do next. Heck, I don't know. I do know WebAPIs have been around so long there are tons of wrong answers on the net.

Let's just show one example that does work and call it a day.

[HttpGet(Name = "GetWeatherForecast")]
public ActionResult<IEnumerable<WeatherForecast>> Get()
{
    try
    {
        return new ActionResult<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }));
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Something failed");
        return BadRequest("that didn't work");
    }
}

Why do I need to wrap the return type with ActionResult to be able to return BadRequest REST code? I don't know man. It should implicitly cast imo into any hard type return value. I mean it's not like we're literally returning IEnumerable<WeatherForecast> in the original code. We are doing some magic behind the scenes to make that a JSON return value for our consumers. Why not do the same for any REST code convenience type too?

Anyhow, I just want to remember this trick for the next time it happens so to the blog it goes.

:sigh:

Labels: , , ,


posted by Jalindrine at 8/23/2024 05:50:00 PM
Wednesday, August 21, 2024

Grubes on the economics of Android and Chrome:

Chrome makes no money at all on its own. Itโ€™s just a funnel for Google Search. Android maybe sort of kind of makes a little money for Google on its own, through the sale of Pixel devices, but itโ€™s negligible. Like Chrome, Android really only exists as a funnel to keep users using Google search and within the broader Google digital ecosystem.

The best counterargument I could come up with was that both serve as first-party digital private investigators, which is likely worth something, though even that ultimately reduces to "broader Google ecosystem" which, itself, also seems to reduce down to search. Does Google sell its behavioral analytics data?

There's an interesting example of the power of this surveillence in The Trust Engineers podcast. Facebook had somewhat naively demonstrated that they had users [nearly everyone?] involved in several A/B style psychological tests at once, and were modifying feeds in ways that seemed to change those users' outlooks on life in general. Horrible ethical optics, and it sounds like potentially horrible ethical outcomes. Can you convince people to shop more? Spend more in specific categories? Give to charities less? Support fringe causes? Change political positions? Break family bonds? Etc.

I guess that's the power that Android and Chrome bring, though there is a bit of an underpants gnomes feel in here somewhere. Either this stuff is so effective I completely miss it or my inclination is accurate: They really don't know how to sell me music, books, goods that I actually like yet, even with all the extra information I've given them, intentionally or not. One day they might make a hard right into exploitation, but so far it doesn't really feel like they're even trying. I might have a profile they sell to companies who buy advertisements, but the advertisements aren't that much more effective than they were 20 years ago, and they should be waaaaay more effective by now!

Still, the point is a very interesting one: What's the long game for these culturally-central open source projects Google backs? Because it's certainly not as simple a profit-seeking setup as, say, selling lemonade on a hot day.

Labels: , , ,


posted by ruffin at 8/21/2024 02:31:00 PM
Sunday, July 21, 2024

Okay, this is pretty seriously "other stuff", but as I'm watching the end of the Tour de France, I figured I'd note I finally pulled in a sprinting jersey in Zwift. Zwift is a virtual bike riding service where you hook your bike onto a "smart trainer" that translates the work you're doing to a computer (I use an iPad mini) connected via bluetooth. It's neat virtual world... the smart trainers make things more difficult when you go up a virtual hill and faster on the way down. It does feel like road biking, but from the safety of my garage.

There are usually sprint segments in each Zwift route in their many virtual worlds, from routes based in Paris to London to Richmond, VA. So if you're going for a 20 mile ride, you might have three sprint segments sprinkled in that route that range from a sixth to a quarter-mile or so.

And if you're the fastest person to have ridden a sprint in the last hour or so, your virtual in-game rider is awarded a "sprint jersey", kind of like sprint jerseys that are awarded in the Tour de France.

I have a love-hate relationship with the sprint segments in the middle of routes. If I try, I can usually get times that land me in the top 5-10% of riders (most of which probably aren't even going out of their way to sprint, to be fair), and I do lots better the shorter the sprint. But then I'm absolutely shot for miles and my overall route time craters. It's a weird risk-reward. It's fun to look like a dope in my garage peddling like a madman to get my name up on a leaderboard, but it's sad seeing my wattage (they measure the power you're producing and put it on your screen at [essentially] all times) crash.

Anyhow, there was a fancy virtual "kit" (biking outfit) that you could get this month on Zwift if you fully ran any two routes in France (Paris or the countryside) this month, give or take, and I was starting my second French-based route this month when a sprint segment rolled up. I knew it'd kill my fairly long ride to sprint hard, but as the starting line appeared, I noticed I was speeding up for a good sprint start. I didn't quite "leave it all out on the road" in the hopes I wouldn't completely ruin the rest of my ride, but it was danged close.

The result? I was one hundredth of a second away from winning the sprint. DAGGUMMIT! This is what you get when you don't quite sell out to something, I guess. Let this be a lesson to you.

But then, about four miles later down the road, it looks like the rider in front of me on the sprint leaderboard dropped offline, promoting you-know-who to sprint leader. AMAZE. Woohoo! I almost didn't notice, but suddenly my usual kit wasn't visible and it had been replaced by a dark green jersey.

Turns out I didn't get the normal bright green Zwift sprint jersey -- maybe because I'm "in France" during the Tour -- I got the official Tour jersey, Skoda green with a Tour logo on the right breast.


It didn't last long; I guess someone else faster beat me a few minutes later. Half of getting the jersey, I believe, is doing it when things are slow, which I think I did. I was riding a less popular route at a less popular time, so anyone with speed could easily beat me. But it was cool to be definitively "the fastest" for a few minutes last week, especially for someone who is not a fast rider overall.

At the very least, Zwift (free for 25 [virtual] km each month!) tricked me into working a little harder by making me think sprinting like a madman on a 25 year-old mountain bike in my garage is fun. ๐Ÿ˜‰

Labels: ,


posted by ruffin at 7/21/2024 11:01:00 AM
Friday, July 05, 2024

I've been using BBEdit as my compare tool on macOS for a few years now, but recently noticed that it keeps opening a window that's maybe 90% of my screen's width and height. That might be useful on a large monitor, but my 13" MacBook Air felt especially cramped.

Welp, to change the default size of a window in BBEdit, you apparently use the menu! Feels very OS 9-. /nostalgia

From "BBEdit > New Window Size & Location > Set Default" on ArsTechnica:

When I began my search for how to set the default location for new windows in BBEdite, I was certain that to set this required a "write defaults" from the command line.

Not so -- no need for BBEdit Expert Preferences for this ... it's in the menu:

Menubar > Window > Save Default <type of> Window

The Save Default Window command stores the position and size of the front window in BBEditโ€™s preferences, and BBEdit will create all new windows of the same type with the stored position and size.

In my case, the "type of" is "Difference".

So, to be overly clear, first open a difference window, size and position it to taste, and then run the "Menubar > Window > Save Default Window" jive.


I'm sure I've mentioned I've been using BBEdit since the year of its birth (not sure exactly, but certainly in 1992. I still fondly remember [a few years later] using it in tandem with Transmit). I'd wandered away from BBEdit for years, using Ultra-Edit on Windows for a while, then VIm, Visual Studio, and a number of language-specific editors (sort of like (and including) PhpStorm), Coda (super briefly), Sublime Text, and now largely (and largely happily) VS Code crossplatform.

It's kinda neat to have a daily use for the "old grey lady" of text editing again and to continue not to be disappointed in its feature-set. BBEdit doesn't suck.

Labels: ,


posted by ruffin at 7/05/2024 06:27:00 PM
Saturday, June 22, 2024

I recently had a process called XProtectRemediatorRedPine eating 95% of one CPU core on my MacBook Air when it wasn't plugged in. That name seemed... strange, and I wondered if it was legit.

Turns out it's probably Apple looking for malware.

The Secrets of XProtectRemediator

Found a blog post called "The Secrets of XProtectRemediator" that has a section called "Reverse Engineering the RedPine Remediator. They apparently picked the Red Pine remediator at random...

With all that background out the way, letโ€™s open up one of the XPR scanners in Binary Ninja and actually see what this looks like. Thereโ€™s no particular reason for choosing RedPine, they all look pretty much the same.

...

Since there are 24 remediators, I wonโ€™t cover them all in detail.

In a results section, they give the "notable results" on Red Pine (and a few others):

Now this one Iโ€™m not totally confident about, but itโ€™s givinggg TriangleDB from Operation Triangulation5. I wonder where Red Pines grow? ๐Ÿฆ…๐Ÿ‡บ๐Ÿ‡ธ

What is TriangleDB?

And there's a link included with more on TriangleDB, which is probably the most interesting link in this blog post. Here's a snippet:

The implant, which we dubbed TriangleDB, is deployed after the attackers obtain root privileges on the target iOS device by exploiting a kernel vulnerability. It is deployed in memory, meaning that all traces of the implant are lost when the device gets rebooted. Therefore, if the victim reboots their device, the attackers have to reinfect it by sending an iMessage with a malicious attachment, thus launching the whole exploitation chain again. In case no reboot occurs, the implant uninstalls itself after 30 days, unless this period is extended by the attackers.

...

Once the implant launches, it starts communicating with the C2 server, using the Protobuf library for exchanging data. The configuration of the implant contains two servers: the primary and the fallback (contained in the lS and lSf configuration fields).

...

The C2 server responds to heartbeat messages with commands. Commands are transferred as Protobuf messages that have type names starting with CRX. The meaning of these names is obscure: for example, the command listing directories is called CRXShowTables, and changing C2 server addresses is handled by the command CRXConfigureDBServer. In total, the implant we analyzed has 24 commands designed for:

  • Interacting with the filesystem (creation, modification, exfiltration and removal of files);
  • Interacting with processes (listing and terminating them);
  • Dumping the victimโ€™s keychain items, which can be useful for harvesting victim credentials;
  • Monitoring the victimโ€™s geolocation;
  • Running additional modules, which are Mach-O executables loaded by the implant. These executables are reflectively loaded, with their binaries stored only in memory.

[emphais mine -mfn]

Overview/quick history of XProtect

The relationship of Red Pines to TriangleDB is supported by another source titled "The Three XProtects of Christmas". It also gives us a good, quick overview of the XProtect system as a whole.

By 2021, Apple had decided to replace MRT with something more modern and capable. The first version of XProtect Remediator was installed in macOS Monterey 12.3 on 14 March 2022, and over the following summer it grew to replace MRT, which was last updated on 29 April 2022, and is now no longer installed with macOS although it may still be obtained as an update.

XProtect Remediator (XPR) is installed and updated outside macOS updates, in the XProtect.app bundle in /Library/Apple/System/Library/CoreServices, in macOS 10.15 Catalina and later. It contains individual executable scanning modules, one of which covers old malware that MRT dealt with. Its first standalone update on 17 June 2022 brought the total of its named malware scanners to eight, and it has grown steadily ever since to a total of 22 in its current version 122.

...

RedPine, added in 114 on 12 October 2023, believed to cover TriangleDB malware;

I'm not going to suggest that I've given you an exhaustive breakdown on what's happening such that you could elevator speech the important parts to someone else (usually my goal), but I think that's enough of a lead for us to figure it out if we wanted to.

In any event, I think this...

  1. Says the process is likely legit.
    • ("likely" because you could always name your malware after something legit)
  2. Makes me think the "efficiency cores" are very efficient, because otherwise this would've killed my battery.
    • Though weird that a process looking for in-memory malware would use so much CPU for so long. ๐Ÿค”

Eh, kinda interesting. I guess this sort of topic helps explain why my blog makes the big bucks.

Labels: ,


posted by ruffin at 6/22/2024 09:35:00 AM
Wednesday, June 19, 2024

Does AirDrop seem to hang when you're sending photos from your iPhone to your Mac? Welp, add "Bringing Devices Together" (I think where you can AirDrop by proximity between two iPhones) to the Apple Fail archives.

After an Apple Support chat session, the agent mentioned something about "bringing the devices together" - I think they assumed the transfer was between 2 iOS devices.

iOS device > General > AirDrop > Bringing Devices Together

I believe it is on by default when upgrading to whichever iOS release contained NameDrop. I manually & specifically turned OFF the setting because of recent "security" articles about the new NameDrop capability

I had this setting turned ON, specifically because I did a contact transfer a week or 2 ago. And accidentally left it on.

Toggling it off, retrying the AirDrop, and it worked.

Seriously, who is in charge of QA/QC at Apple? How is sending photos from an iPhone to a Mac not on your integration test list?

Labels: , ,


posted by ruffin at 6/19/2024 05:31:00 PM
Monday, June 10, 2024

Okay, was looking for email corpi (corpuses? No, apparently corpora) to run some tests, and found these...

Check the licenses for each and enjoy.

Labels: , ,


posted by ruffin at 6/10/2024 05:14:00 PM
Thursday, June 06, 2024

I used a Henge Dock for my old Intel MacBook and, other than some scratches on about 40% of the outside of the laptop's cover, surprisingly really liked it. It was a real space-saver for the desk. Though I'd rather have had the laptop screen available in addition to the external monitor, I didn't use it that often at the desk, and the dock was a great compromise.

Fast-forward a bit to me buying an M1 MacBook Air. I've been jealously eyeing the new-ish Brydge Vertical Dock since the M1 design was still housing Intel processors. Brydge apparently bought out Henge, and they're doing slightly fancier versions of the same sort of clamshell docks, but, um, wow did it get expensive!

Good things come to those who wait for the price to drop, and I recently bagged one on Amazon for the M1 for around $30, down from a height of $275!!!

But that's not why you're here. You're here because you might've done the same and you want to know why you can't get your MacBook to display to an external monitor. You've plugged in a keyboard and/or mouse, slammed shift and the mouse buttons, and nothing. Is your HDMI cord bad? Is the Mac still asleep? Is there a setting that you needed to set?

NO!! It's so much simpler!!! You have to have it plugged into a power supply!!

Or, more to the point, clamshell mode doesn't work when the MacBook is on battery power.

Why not when it'll run for 5-8 hours on the battery? And how useful would it be to be able to test that the dock is working before setting up a power supply behind the desk? Very. Or maybe you just want to use the paltry two USB-C ports for something other than power or a dock with power pass-through. TOO BAD!

I have no idea why clamshell on battery doesn't work and isn't, afaict, even a setting. But it doesn't.

I've done this twice now after not using the Brydge for a few months in between. /sigh (Told you using the MacBook with the external monitor is rare.)

Labels: ,


posted by ruffin at 6/06/2024 04:41:00 PM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.