Skip to content

Commit

Permalink
finish version 0.4 with major changes
Browse files Browse the repository at this point in the history
  • Loading branch information
henderea committed Jul 20, 2014
1 parent 4f2b4da commit 406a17b
Show file tree
Hide file tree
Showing 14 changed files with 1,133 additions and 23 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
MemoryTamer
===========

A RubyMotion application for keeping memory usage in check.
A RubyMotion application for keeping memory usage in check. Shows up in the menu bar and shows current free ram (refreshed every 2 seconds).

**Note:** I got the name idea from App Tamer (<http://www.stclairsoft.com/AppTamer/>), but I don't plan to sell this or distribute it widely, so hopefully the naming won't be an issue.

###Versions:
* v0.3: <https://myepg-ds.s3.amazonaws.com/MemoryTamer-0.3>
**Note:** The plain allocation method uses the C code from <http://forums.macrumors.com/showpost.php?p=8941184&postcount=54>

###Release notes
* **v0.3:** Initial public release. Only supports Mavericks and up because it uses the memory_pressure command introduced then. Configuration is in a plain text file in the home directory and only checked on startup.
* **v0.4:** Significant changes. Now compiled to work all the way back to Lion (though not tested on anything other than Mavericks). Has more configurations options and now lets you change them at runtime in the menu. Besides memory threshold and freeing pressure, you can now control the notification system (Growl or Notification Center) and the freeing method (memory_pressure or plain allocation).

###Versions (code-signed with developer ID):
* **v0.3:** <https://myepg-ds.s3.amazonaws.com/MemoryTamer-0.3> (Mavericks-only)
* **v0.4:** <https://myepg-ds.s3.amazonaws.com/MemoryTamer-0.4> (should work back to Lion)
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Motion::Project::App.setup do |app|
app.info_plist['NSUIElement'] = 1
app.deployment_target = '10.7'
app.codesign_certificate = 'Developer ID Application: Eric Henderson (SKWXXEM822)'
app.embedded_frameworks << 'vendor/Growl.framework'
end
86 changes: 66 additions & 20 deletions app/app_delegate.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
class AppDelegate
attr_accessor :free_display_title

# noinspection RubyUnusedLocalVariable
def applicationDidFinishLaunching(notification)
@freeing = false
@mavericks = `which memory_pressure`.include?('memory_pressure')
@has_nc = true
system('which memory_pressure')
@mavericks = $?.success?
@has_nc = (NSClassFromString('NSUserNotificationCenter')!=nil)
load_prefs
MainMenu.build!
MainMenu[:statusbar].subscribe(:status_free) { |_, _|
Expand All @@ -16,18 +18,24 @@ def applicationDidFinishLaunching(notification)
MainMenu[:prefs].subscribe(:preferences_refresh) { |_, _|
NSLog 'Reloading preferences'
load_prefs
set_notification_display
set_mem_display
set_pressure_display
set_method_display
}
MainMenu[:prefs].subscribe(:notification_change) { |_, _|
@settings[:growl] = !@settings[:growl]
save_prefs
set_notification_display
}.canExecuteBlock { |_| @has_nc }
MainMenu[:prefs].subscribe(:memory_change) { |_, _|
nm = get_input('Please enter the memory threshold in MB (non-negative integer only)', "#{@settings[:mem]}")
nm = get_input("Please enter the memory threshold in MB (0 - #{get_total_memory / 1024**2})", "#{@settings[:mem]}") { |str| (str =~ /^\d*$/) }
begin
nmi = nm.to_i
if nmi < 0
alert('The memory threshold must be non-negative!')
elsif nmi > get_total_memory / 1024**2
alert('You can\'t specify a value above your total ram')
else
@settings[:mem] = nmi
save_prefs
Expand All @@ -43,14 +51,23 @@ def applicationDidFinishLaunching(notification)
@settings[:pressure] = np
save_prefs
set_pressure_display
else
alert("Invalid option '#{np}'!")
end
}.canExecuteBlock { |_| @mavericks }
MainMenu[:prefs].subscribe(:method_change) { |_, _|
@settings[:method_pressure] = !@settings[:method_pressure]
save_prefs
set_method_display
}.canExecuteBlock { |_| @mavericks }
set_notification_display
set_mem_display
set_pressure_display
NSUserNotificationCenter.defaultUserNotificationCenter.setDelegate(self)
set_method_display
NSUserNotificationCenter.defaultUserNotificationCenter.setDelegate(self) if @has_nc
GrowlApplicationBridge.setGrowlDelegate(self)
@statusItem = MainMenu[:statusbar].statusItem
NSLog "Starting up with memory = #{dfm}; pressure = #{@options[:pressure]}"
NSLog "Starting up with memory = #{dfm}; pressure = #{@settings[:pressure]}"
Thread.start {
@last_free = NSDate.date - 120
loop do
Expand All @@ -75,7 +92,12 @@ def set_mem_display

def set_pressure_display
MainMenu[:prefs].items[:pressure_display][:title] = "Freeing pressure: #{@settings[:pressure]}"
MainMenu[:prefs].items[:pressure_change][:title] = @mavericks ? 'Change freeing pressure' : 'Requires Mavericks 10.9'
MainMenu[:prefs].items[:pressure_change][:title] = @mavericks ? 'Change freeing pressure' : 'Requires Mavericks 10.9 or higher'
end

def set_method_display
MainMenu[:prefs].items[:method_display][:title] = "Freeing method: #{@settings[:method_pressure] ? 'memory pressure' : 'plain allocation'}"
MainMenu[:prefs].items[:method_change][:title] = @mavericks ? "Use #{!@settings[:method_pressure] ? 'memory pressure' : 'plain allocation'} method" : 'Requires Mavericks 10.9 or higher to change'
end

def load_prefs
Expand All @@ -87,8 +109,10 @@ def load_prefs
tmp = YAML::load(fc)
@settings[:mem] = tmp[:mem] if tmp[:mem] && tmp[:mem].is_a?(Numeric)
@settings[:pressure] = tmp[:pressure] if tmp[:pressure] && %w(normal warn critical).include?(tmp[:pressure])
@settings[:growl] = tmp[:growl]
@settings[:method_pressure] = tmp[:method_pressure]
@settings[:growl] = tmp[:growl] && tmp[:growl] != 0
@settings[:method_pressure] = tmp[:method_pressure] && tmp[:method_pressure] != 0
else
save_prefs
end
rescue
# ignored
Expand All @@ -108,10 +132,10 @@ def dfm

def free_mem_default(cfm)
@freeing = true
notify 'Beginning memory freeing'
free_mem(@options[:pressure])
notify 'Beginning memory freeing', 'Start Freeing'
free_mem(@settings[:pressure])
nfm = get_free_mem
notify "Finished freeing #{format_bytes(nfm - cfm)}"
notify "Finished freeing #{format_bytes(nfm - cfm)}", 'Finish Freeing'
NSLog "Freed #{format_bytes(nfm - cfm, true)}"
@freeing = false
@last_free = NSDate.date
Expand Down Expand Up @@ -140,11 +164,15 @@ def get_memory_pressure
`/usr/sbin/sysctl kern.memorystatus_vm_pressure_level`.chomp.to_i
end

def get_total_memory
`/usr/sbin/sysctl hw.memsize`.chomp.to_i
end

def free_mem(pressure)
if @mavericks
if @settings[:method_pressure]
cmp = get_memory_pressure
if cmp >= 4
notify 'Memory Pressure too high! Running not a good idea.'
notify 'Memory Pressure too high! Running not a good idea.', 'Error'
return
end
dmp = pressure == 'normal' ? 1 : (pressure == 'warn' ? 2 : 4)
Expand Down Expand Up @@ -180,6 +208,13 @@ def get_input(message, default_value, type = :text, options = [])
input = NSComboBox.alloc.initWithFrame(NSMakeRect(0, 0, 200, 24))
input.addItemsWithObjectValues(options)
input.selectItemWithObjectValue(default_value)
when :number
input = NSTextField.alloc.initWithFrame(NSMakeRect(0, 0, 200, 24))
input.stringValue = "#{default_value}"
formatter = NSNumberFormatter.alloc.init
formatter.allowsFloats = false
formatter.minimum = 0
formatter.maximum = get_total_memory / 1024**2
else
input = NSTextField.alloc.initWithFrame(NSMakeRect(0, 0, 200, 24))
input.stringValue = default_value
Expand Down Expand Up @@ -207,12 +242,23 @@ def alert(message)
alert.runModal
end

def notify(msg)
NSLog "Notification: #{msg}"
notification = NSUserNotification.alloc.init
notification.title = 'MemoryTamer'
notification.informativeText = msg
notification.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.defaultUserNotificationCenter.scheduleNotification(notification)
def notify(msg, nn)
NSLog "Notification (#{nn}): #{msg}"
if @settings[:growl]
GrowlApplicationBridge.notifyWithTitle(
'MemoryTamer',
description: msg,
notificationName: nn,
iconData: nil,
priority: 0,
isSticky: true,
clickContext: nil)
else
notification = NSUserNotification.alloc.init
notification.title = 'MemoryTamer'
notification.informativeText = msg
notification.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.defaultUserNotificationCenter.scheduleNotification(notification)
end
end
end
20 changes: 20 additions & 0 deletions resources/Growl Registration Ticket.growlRegDict
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>TicketVersion</key>
<integer>1</integer>
<key>AllNotifications</key>
<array>
<string>Start Freeing</string>
<string>Finish Freeing</string>
<string>Error</string>
</array>
<key>DefaultNotifications</key>
<array>
<string>Start Freeing</string>
<string>Finish Freeing</string>
<string>Error</string>
</array>
</dict>
</plist>
1 change: 1 addition & 0 deletions vendor/Growl.framework/Growl
1 change: 1 addition & 0 deletions vendor/Growl.framework/Headers
1 change: 1 addition & 0 deletions vendor/Growl.framework/Resources
Binary file added vendor/Growl.framework/Versions/A/Growl
Binary file not shown.
5 changes: 5 additions & 0 deletions vendor/Growl.framework/Versions/A/Headers/Growl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <Growl/GrowlDefines.h>

#ifdef __OBJC__
# include <Growl/GrowlApplicationBridge.h>
#endif
Loading

0 comments on commit 406a17b

Please sign in to comment.