Faster to write a script than to deal with so many browser windows? Well, certainly more fun :)

AppleScript to move Safari windows up as much as the window closest to the top of the screen will allow.

There’s a bug in Safari 15.0 where it sometimes re-positions open windows when waking from sleep by moving all the windows down an amount such that the bottom of the lowest one is at the bottom of the monitor (or something like that? They all move down a lot).

If happen to place your Safari windows up enough so that another app’s window can be visible on-screen under them (chat in my case), and you have a few too many windows open in Safari (ahem!), then you might be very annoyed to have to over a hundred windows all back up again.

Now, you could go through your heap of open Safari windows, but if you’re a programmer than you might (likely incorrectly ;)) think it’d be faster to write an AppleScript to move them all back up again. Not that I’d do such a thing, you understand, but, purely for educational purposes, here’s the kind of script one might write (or the second version if the first one took 6 minutes process the > 100 Safari windows you had open) :). This one moves all Safari windows up the same amount to keep their relative positioning and moves them up as much as possible while still keeping the window positioned highest on the screen below the menu bar.

Disclaimer: I write zero to two AppleScripts a year and never really learned AppleScript. There’s almost certainly better ways to do this. But it works for me and fixes my windows after Safari munges them again. So GoodEnough™ 🙂

-- Put in the public domain with zero warranty of any kind
--
-- only tested on macOS 11.6, with Safari 13.0; ymmv

use scripting additions

tell application "Safari"
  set windlist to windows
  
  log "Examining " & length of windlist & " windows..."
  
  set lowestYWindow to first item of windlist
  set lowestYValue to get second item of (get bounds of lowestYWindow)
  
  repeat with wind in windlist
    set curValue to second item of (get bounds of wind)
    if curValue < lowestYValue then
      copy wind to lowestYWindow
      set lowestYValue to curValue
    end if
  end repeat
  
  -- subtract out the title bar height (hard coded - bad dog!)
  set yOffset to lowestYValue - 25
  
  if yOffset > 0 then
    log "moving " & length of windlist & " windows up " & yOffset & " pixels"
    repeat with aWind in windlist
      set aBounds to (get the bounds of aWind)
      set the second item of aBounds to ((get the second item of aBounds as integer) - yOffset)
      set the fourth item of aBounds to ((get the fourth item of aBounds as integer) - yOffset)
      set the bounds of aWind to aBounds
    end repeat
  else
    display alert ("The highest window already at the top!  Not moving any windows!")
  end if
  log "done"
end tell

Here it is in a gist for easier copy/edit, etc.

7DRL 2014 – Day 7: In which everything is complete

Trinkets – the roguelike game I’ve spent the past week creating for the 7DRL competition – is finished, about 20 hours before the deadline! It’s a game about wandering an extra dimensional vault, acquiring trinkets and trying to get back home.

A build for mac is available here. Unfortunately, there’s no windows/linux build, but if you’re on one of those operating systems and want to play, the source is available here.

Screen Shot 2014-03-15 at 2.38.31 PM

… which turns out to be entirely justified.

My high score is 59 – can you beat it? If so, let me know in the comments below!

7DRL 2014 – Day 6: In which victory approaches at high velocity

One day remains, and I’m actually almost done. It would be nice to redo the map generator, and game balance can always be tweaked some more, but the plot and endgame are implemented, the boss-fights are done with basic AI, and there’s even a score feature. Shown below is the second of four boss fights:

You have a bad feeling about this level...
You have a bad feeling about this level…

… which turns out to be entirely justified.
… which turns out to be entirely justified.

7DRL 2014 – Day 5: In which parkour is performed in fancy shoes

Today was very productive; I finished the procedural generation of trinkets (for now, at least), improved the magic system, procedurally generated strangely worded descriptions of said trinkets, added new effects including knock-back and circular slide, and added all of the non-boss enemies, including the Abberant visible below:

Image
Parkour shoes acquired!

Shown above is the non-procedurally-generated description of one of the coolest shoe types in the game (thus far, at least). These shoes allow you to run along walls, moving faster than enemies or even jumping over them.

I still have two more days left to go, and quite a bit still to do, including:

  • Plot
  • Victory conditions
  • Boss fights + Boss AI
  • Game balance
  • Prettier maps

7DRL 2014 – Days 3 and 4: In which trinkets are created

So, I’m about halfway through the week, but days 2-4 were all mostly occupied by other things, so the real productivity is just beginning. I’ve reworked the combat system to use the same generalized targeting/effect model as the spells. I’ve also changed trinket abilities to work on that model, divided trinkets into six types (hats, orbs, wands, cloaks, weapons, and shoes), which can be equipped and used for various nefarious purposes. I’ve started work on the procedural generation of trinkets, but there’s still a lot to do on that front, which will occupy the next day and a half, leaving me with a day and a half for enemies, plot, game balance, and a dungeon generator that isn’t stolen from the libtcod tutorial. That’s plenty of time, right?

Screen Shot 2014-03-12 at 11.36.07 PM
Weapons can now replace your bump attack with one or two spell casts. This one replaces your attack with two fire bolts, but they can also sunder defenses or daze and freeze enemies, among other things.

Screen Shot 2014-03-11 at 9.38.44 PM
Death Star? Death Star! DEATH STAR!
This is what happens when you procedurally generate names by picking two words from a list and concatenating them…

 

7DRL Competition 2014 – Day 2: In which the art of sorcery is practiced

Screenshot of Trinkets
Magic system: Activate!

There are now six spells, and status effects, and some of the ‘everything goes horribly wrong’.

The spells have all been divided into ‘targeting’ and ‘effect’ components – except for teleportation, which still consists of duct tape and hacks and probably will remain that way.

There’s also a bit more user interface than before, and the trinkets are actually worth using sometimes (even if they are all still exactly identical).

Melee combat has also been re-written to allow for spells like the Ethereal Axe, which replaces your melee attack with a high-damage Arcane attack.

Tomorrow, I’ll be working on equipment. I won’t have much time though – classes and whatnot – so there probably won’t be a blog post.

The day after, procedural trinkets, more enemies, and everything going even more horribly wrong!

7DRL Competition 2014 – Day 1: In which a roguelike is begun

This year I’m participating in the Seven Day Roguelike Competition, a contest to make a game in the roguelike genre in a week. (For those not familiar with it, roguelikes tend to be dungeon-crawlers with procedurally generated words, permanent character death, and often old-school graphics. There are frequently some variations from this rough description.)

I’m making a game known as ‘Trinkets’ until I think of a better name. It is a game in which a wizard strapped for cash loots a pocket dimension, and everything goes horribly wrong. It will experiment with a nontraditional power progression; you become weaker, then strong again, rather than a constant power level or steady increase. There will be a plot and randomly generated Trinkets.

My time will be somewhat limited, as I am currently a college student, but after spending most of today programming I produced the following:

Screenshot of Trinkets
Taste the power, troll scum!

I’ve used some code from some past projects, specifically most of the data structures and UI code. I’m using python, with the libtcod library.

Current features:

  • Move around…
  • …on a procedurally generated map…
  • …and hit things…
  • …which hit you back, and can kill you if you’re not careful.
  • Look for trinkets…
  • …which have magic powers…
  • …which are simply not as powerful (at least for now) as your spells.
  • Speaking of spells, you have five (and will have six), which in combination make you nearly unstoppable…
  • …at least until I implement the ‘everything goes horribly wrong’ part.

NSURLConnection & GDC

Using NSURLConnection combined with GCD for the first time and noticed that if you invoke NSURLConnection on anything other than the main queue it seems to lock up and nothing happens.  Actually something happens but your delegate never gets called.  Easy fix though; you just need to set the delegate queue (or run loop) before starting the download.

 

Here’s some sample code setting the delegate queue for you to play with if you like. Just make a sample cocoa app from the standard template and then setup the application delegate like this


@interface AppDelegate ()
  @property (strong) NSURLConnection* connection;
  @property (strong) NSMutableData * downloadedData;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
  // Insert code here to initialize your application

  dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), 
   ^{  
      self.downloadedData = [NSMutableData new];
      NSURLRequest * request = [NSURLRequest requestWithURL: 
      [NSURL URLWithString: @"http://wp.me/av6Bm-pZ"]];
      self.connection = [[NSURLConnection alloc] initWithRequest: request 
                          delegate: self 
                        startImmediately: NO];  // This *must* be "NO". 
                                                // Can't switch delegate queue 
                                                // after download starts
      [self.connection setDelegateQueue: [NSOperationQueue mainQueue]];  // *** set queue
      [self.connection start];   // *now* you can start the fetch
      NSLog(@"request queued!");
   });
}

// delegate calls just so let us know when it's working or when it isn't

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog( @"download failed with an error: %@, %@", 
        error, [error description] );

    // release this stuff, test is done.
    self.connection = nil;
    self.downloadedData = nil;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog( @"connection did receive response: %@", response );
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"download data received %lu", [data length] );
    [self.downloadedData appendData: data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"didFinishLoading.  total data is: %lu", [self.downloadedData length] );

    // release this stuff, test is done.
    self.connection = nil;
    self.downloadedData = nil;
}

@end

Line 22 is the key (the setDelegateQueue: call). If you leave that line out then you’ll wonder why it’s not downloading your data. If you change the queue passed to line 12 with dispatch_get_main_queue() then it’ll work without the setDelegateQueue: call).

Obvious once you see it but maybe this’ll save someone a bit of time.

P.S. This was done with Xcode 5 under 10.8.5 fwiw.