So I've been trying to use CGPostMouseEvent, and CGEventPostToPSN to send a mouse click to a mac game, and unfortunately have been very unsuccessful.
I was hoping someone may be able to help me think of this differently, or realize what I'm missing. Google hasn't been much help.
My guess is that it's because I'm trying to send a click event to a game window (openGL), vs. a normal window.
Here is another example of what I'm trying to send:
CGEventRef CGEvent;
NSEvent *customEvent;
NSPoint location;
location.x = 746;
location.y = 509;
customEvent = [NSEvent mouseEventWithType: NSLeftMouseDown
location: location
modifierFlags: NSLeftMouseDownMask
timestamp: time(NULL)
windowNumber: windowID
context: NULL
eventNumber: 0
clickCount: 1
pressure: 0];
CGEvent = [customEvent CGEvent];
CGEventPostToPSN(&psn, CGEvent);
Interestingly enough, I can move the mouse fine (CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, clickPt);), I just can't send any clicks :/
Any help would be greatly appreciated.
Edit: Here is what is strange, once I move the mouse using CGDisplayMoveCursorToPoint, I actually have to physically move my mouse up or down a hair before I can even click, which is odd. The game doesn't accept any input unless I move it up/down (and the pointer then changes).
Thanks!
Well what you are try to build is "bot" or "robot" which basically sends commands in an orderly fashion to a game. Basically it will play for you as you are afk. This is great for games that force you to play to harvest minerals, commodities or whatever gives you money to advance in the game. Which is really kind of boring. I have successfully done this for a popular game, although i cannot mention the game as it breaks the user agreements which all these type of games have against "bots". So beware of what you are doing, as it may break your user agreement for many MMPG. But i post this successfully here because, the Mac has less bots available, none that i have been able to research, vs the PC which i have found many. So to level the playing field.. here is the code. I recommend to compile it as command line, and execute the macro in AppleScript (were the logic will reside on how to mimic the games click mouses, movements and send keys, basically your AI.
1.- First you need to run class that will get your psn "process serial number" which all games have. Basically what Thread it is running at. You can find out the name of the process in the utility in the Mac called "Activity Monitor". This can also be done easily in AppleScript.
Once you have the name, this class will locate and give you back its psn.
#import <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#include <stdio.h>
#interface gamePSN : NSObject
{
ProcessSerialNumber gamePSN;
ProcessInfoRec gameProcessInfo;
pid_t gameUnixPID;
}
- (ProcessSerialNumber) gamePSN;
- (ProcessInfoRec) gameProcessInfo;
- (pid_t) gameUnixPID;
- (void) getPSN;
#end
#implementation gameSN
- (ProcessSerialNumber) gamePSN { return gamePSN; }
- (ProcessInfoRec) gameProcessInfo { return gameProcessInfo; }
- (pid_t) gameUnixPID; { return gameUnixPID; }
- (void) getPSN
{
auto OSErr osErr = noErr;
auto OSErr otherErr = noErr;
auto ProcessSerialNumber process;
auto ProcessInfoRec procInfo;
auto Str255 procName;
auto FSSpec appFSSpec;
auto char cstrProcName[34];
auto char one ='G'; // FIRST CHARCTER OF GAME PROCESS NAME THESE NEED TO BE CHANGED AS I PUT IN FAKES
auto char two ='A'; // SECOND CHARACTER OF GAME PROCESS NAME THESE NEED TO BE CHANGED AS I PUT IN FAKES
auto char three = 'M'; // THIRD CHARACTER OF GAME PROCESS NAME THESE NEED TO BE CHANGED AS I PUT IN FAKES
auto unsigned int size;
process.highLongOfPSN = kNoProcess;
process.lowLongOfPSN = kNoProcess;
procInfo.processInfoLength = sizeof(ProcessInfoRec);
procInfo.processName = procName;
procInfo.processAppSpec = &appFSSpec;
while (procNotFound != (osErr = GetNextProcess(&process))) {
if (noErr == (osErr = GetProcessInformation(&process, &procInfo))) {
size = (unsigned int) procName[0];
memcpy(cstrProcName, procName + 1, size);
cstrProcName[size] = '\0';
// NEEDS TO MATCH THE SIGNATURE OF THE GAME..FIRST THREE LETTERS
// IF YOU CANT FIND IT WITH THE ACTIVITY MONITOR UTILITY OF APPLE MAC OS
// THEN RUN THIS SAME CLASS WITH AN NSLOG AND IT WILL LIST ALL YOUR RUNNING PROCESSES.
if ( (((char *) &procInfo.processSignature)[0]==one) &&
(((char *) &procInfo.processSignature)[1]==two) &&
(((char *) &procInfo.processSignature)[2]==three) &&
(((char *) &procInfo.processSignature)[3]==two))
{
gamePSN = process;
otherErr = GetProcessInformation(&gamePSN, &gameProcessInfo);
otherErr = GetProcessPID(&process, &gameUnixPID);
}
}
}
}
Once you have this process number it is easy to send key events as well as mouse events. Here is the mouse event clicks to send.
// mouseClicks.h
// ClickDep
// Created by AnonymousPlayer on 9/9/11.
#import <Foundation/Foundation.h>
#interface mouseClicks : NSObject
- (void) PostMouseEvent:(CGMouseButton) button eventType:(CGEventType) type fromPoint:(const CGPoint) point;
- (void) LeftClick:(const CGPoint) point;
- (void) RightClick:(const CGPoint) point;
- (void) doubleLeftClick:(const CGPoint) point;
- (void) doubleRightClick:(const CGPoint) point;
#end
/
// mouseClicks.m
// ClickDep
// Created by AnonymousPlayer on 9/9/11.v
#import "mouseClicks.h"
#implementation mouseClicks
- (id)init
{
self = [super init];
if (self) {
// Initialization code here if you need any.
}
return self;
}
- (void) PostMouseEvent:(CGMouseButton) button eventType:(CGEventType) type fromPoint:(const CGPoint) point;
{
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, type, point, button);
CGEventSetType(theEvent, type);
CGEventPost(kCGHIDEventTap, theEvent);
CFRelease(theEvent);
}
- (void) LeftClick:(const CGPoint) point;
{
[self PostMouseEvent:kCGMouseButtonLeft eventType:kCGEventMouseMoved fromPoint:point];
NSLog(#"Click!");
[self PostMouseEvent:kCGMouseButtonLeft eventType:kCGEventLeftMouseDown fromPoint:point];
sleep(2);
[self PostMouseEvent:kCGMouseButtonLeft eventType:kCGEventLeftMouseUp fromPoint:point];
}
- (void) RightClick:(const CGPoint) point;
{
[self PostMouseEvent:kCGMouseButtonRight eventType:kCGEventMouseMoved fromPoint:point];
NSLog(#"Click Right");
[self PostMouseEvent:kCGMouseButtonRight eventType: kCGEventRightMouseDown fromPoint:point];
sleep(2);
[self PostMouseEvent:kCGMouseButtonRight eventType: kCGEventRightMouseUp fromPoint:point];
}
- (void) doubleLeftClick:(const CGPoint) point;
{
[self PostMouseEvent:kCGMouseButtonRight eventType:kCGEventMouseMoved fromPoint:point];
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, point, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, theEvent);
sleep(2);
CGEventSetType(theEvent, kCGEventLeftMouseUp);
CGEventPost(kCGHIDEventTap, theEvent);
CGEventSetIntegerValueField(theEvent, kCGMouseEventClickState, 2);
CGEventSetType(theEvent, kCGEventLeftMouseDown);
CGEventPost(kCGHIDEventTap, theEvent);
sleep(2);
CGEventSetType(theEvent, kCGEventLeftMouseUp);
CGEventPost(kCGHIDEventTap, theEvent);
CFRelease(theEvent);
}
- (void) doubleRightClick:(const CGPoint) point;
{
[self PostMouseEvent:kCGMouseButtonRight eventType:kCGEventMouseMoved fromPoint:point];
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, point, kCGMouseButtonRight);
CGEventPost(kCGHIDEventTap, theEvent);
sleep(2);
CGEventSetType(theEvent, kCGEventRightMouseUp);
CGEventPost(kCGHIDEventTap, theEvent);
CGEventSetIntegerValueField(theEvent, kCGMouseEventClickState, 2);
CGEventSetType(theEvent, kCGEventRightMouseDown);
CGEventPost(kCGHIDEventTap, theEvent);
sleep(2);
CGEventSetType(theEvent, kCGEventRightMouseUp);
CGEventPost(kCGHIDEventTap, theEvent);
CFRelease(theEvent);
}
#end
You may need to play with the sleep which is the time interval between pushing the mouse button and releasing it. I have found that using 1 second sometimes it does not do it. Putting 2 seconds make it work all the time.
So your main would to the following.
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// Grabs command line arguments -x, -y, -clicks, -button
// and 1 for the click count and interval, 0 for button ie left.
int x = [args integerForKey:#"x"];
int y = [args integerForKey:#"y"];
int clicks = [args integerForKey:#"clicks"];
int button = [args integerForKey:#"button"];
//int interval= [args integerForKey:#"interval"];
int resultcode;
// PUT DEFAULT VALUES HERE WHEN SENT WITH EMPTY VALUES
/*if (x==0) {
x= 1728+66;
y= 89+80;
clicks=2;
button=0;
}
*/
// The data structure CGPoint represents a point in a two-dimensional
// coordinate system. Here, X and Y distance from upper left, in pixels.
CGPoint pt;
pt.x = x;
pt.y = y;
// Check CGEventPostToPSN Posts a Quartz event into the event stream for a specific application.
// only added the front lines plus changed null in Create Events to kCGHIDEventTap
gamePSN *gameData = [[gamePSN alloc] init];
[gameData getPSN];
ProcessSerialNumber psn = [gameData gamePSN];
resultcode = SetFrontProcess(&psn);
mouseClicks *mouseEvent =[[mouseClicks alloc] init];
if (button == 0)
{
if (clicks==1) {
[mouseEvent LeftClick:pt];
} else {
[mouseEvent doubleLeftClick:pt];
}
}
if (button == 1)
{
if (clicks==1) {
[mouseEvent RightClick:pt];
} else {
[mouseEvent doubleRightClick:pt];
}
}
[gameData release];
[mouseEvent release];
[pool drain];
return 0;
}
Hope this is helpful.... remember you can execute this in the terminal or in AppleScript by sending the following command.
do shell script "/...Path to Compiled Program.../ClickDep" & " -x " & someX & " -y " & someY & " -clicks 1 -button 1"
HAPPY GAMING!!!!
Related
I have a Xcode 5/Cocoa program that clicks the left mouse button after specified interval a specified number of times. That part works fine. The problem occurs when I want to stop the while loop prematurely.
I'm using a listener to detect any key press during the running of the program, set a stopnow variable and check for that variable in the while loop. But, the while loop doesn't detect the change in the variable until the loop finishes.
Also, I change a counter in the title bar of the window to display the count of clicks done, and that doesn't get updated either until the loop finishes.
I do get the NSLog message when I press a key.
I'm very confused.
My code is here :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[[self myWindow] setLevel:NSFloatingWindowLevel];
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) {
keychar = (unichar) event.characters;
[NSApp activateIgnoringOtherApps:YES];
stopnow = 1;
NSLog(#"Key Pressed = x%x (%x) (%x)",keychar,(keychar&0x7f00),((keychar&0xff00)>>8));
}];
}
- (IBAction)setClickPoint:(NSButton *)sender {
sleep(5);
CGEventRef ourEvent = CGEventCreate(NULL);
cgPoint = CGEventGetLocation(ourEvent);
myPoint = [NSString stringWithFormat:#" (%5.0f,%5.0f)", cgPoint.x, cgPoint.y];
myNewTitle = [mytitle stringByAppendingString:myPoint];
[[self myWindow] setTitle:myNewTitle];
}
(IBAction)strtButton:(NSButton *)sender {
NSLog(#"Entered strButtn");
numClicks = [_nClicks intValue];
numWait = [_nWait floatValue];
i = 0;
while (i < numClicks || numClicks == 0) {
i++;
myTotal = [NSString stringWithFormat:#" %i of %i", i, numClicks];
myNewTitle = [mytitle stringByAppendingString:myPoint];
myNewTitle = [myNewTitle stringByAppendingString:myTotal];
[[self myWindow] setTitle:myNewTitle];
CGWarpMouseCursorPosition(cgPoint);
CGEventRef down = CGEventCreateMouseEvent(0, kCGEventLeftMouseDown,cgPoint, 0);
CGEventPost(kCGSessionEventTap, down);
CFRelease(down);
CGEventRef up = CGEventCreateMouseEvent(0, kCGEventLeftMouseUp,cgPoint, 0);
CGEventPost(kCGSessionEventTap, up);
CGRealease(up);
NSLog(#"stopnow = %i", stopnow);
if (stopnow == 1) {
stopnow = 0;
break;
}
usleep((unsigned int)(numWait * 1000000.0));
}
}
A Cocoa/Cocoa Touch app is an event-based environment, so you cannot have long running "loops" in the main thread, as you stop the handling and delivery of the events.
When your loop finishes, the UI is able to update the bits you are seeing, as it can now deliver the events.
You will need to do this work in the background thread, or some such.
Ok, here is what works - use dispatch_async(global type) for the main loop, use dispatch_async(main queue) for the code that updates the title.
I am new at Objective C and ay programmer's world in general (2 or 3 weeks). Also I am Italian, and so It's quite difficult for me using english and the right terminology.
I created a class: "Mano" (Hand) with its instances and methods: m1 and m2.
My question is: How can I do to read m1 variables value with m2 ?
m1 and m2 have for example "int a; a=0;"
then m1, executing a method, sets a = 10
how can m2 read m1's a? Becouse if m2 simply reads a it reads his own a ... a=0, right?
Sorry again but I don't know how to explain myself better than this!
This was meant to be a simple RPS (Rock Paper Scissors) game just to start learning a little Objective C programming. In the beginning there were two objects m1 and m2 as the “Computer hand m1” and the “Player hand m2”.
Of course as you can see i joined the two methods (computer random number and human number choosing) so that all de job is made by just one object.
When methods were splitted I had problems in the [verdetto] (game results) methods because of m2 (jn my case) were not able to get the right value of the object instance (i needed m1 values) …
and that’s why I asked you my question!
Here you have 0=Rock 1=Scissors 2=Paper
// File main.m
#import <Foundation/Foundation.h>
#import "mano.h"
int main(int argc, const char * argv[])
{
int contatore;
contatore = 1;
#autoreleasepool {
mano *m1;
m1 = [[ mano alloc ] init ];
// mano *m2;
// m2 = [[ mano alloc ] init ];
while (contatore <= 5) {
NSLog(#"\n Partita N. %d", contatore);
contatore++;
[m1 gioco];
[m1 controlloRisultato]; }
// [m1 generatoreRandom] RANDOM NUMBER GENERATOR to let the computer choose
// [m2 scelta numero]here the player was meant to choose bw 0,1,2.
[m1 verdetto]; //game results
}
return 0;
}
// File mano.h
#import <Foundation/Foundation.h>
#interface mano : NSObject {
NSArray *simboli; //Rock Scissors Paper
NSInteger casuale; // Random Number
NSInteger scelta; // Player Number
NSInteger computer; // CPU wins
NSInteger giocatore; // Player Wins
NSInteger pareggi; // Draws
}
- (void) gioco;
- (void) controlloRisultato;
- (void) verdetto;
// - (void) generatoreRandom
// - (void) scelta numero
#end
// File mano.m
#import "mano.h"
#implementation mano
- (id) init {
self = [super init];
casuale = 0;
scelta = 0;
simboli = [ [ NSArray alloc] initWithObjects: #"Sasso",#"Forbici",#"Carta", nil]; //questo va nella init, giustamente, altrimenti non vale per tutti i metodi.
return self; }
- (void) gioco {
scelta =0;
casuale =0;
casuale = arc4random_uniform(3); //il computer sceglie un numero
// L'utente fa la sua scelta qui sotto
do {
NSLog (#"\n \n Inserisci 0 per Sasso, 1 per Forbici, 2 per Carta");
scanf ("%ld", &scelta);
if (scelta >2 || scelta <0) NSLog (#" \n Hai inserito un numero NON VALIDO");
} while (scelta >2 || scelta <0);
}
- (void) controlloRisultato {
NSLog(#"\nIl computer ha scelto: %#", [simboli objectAtIndex:casuale]);
NSLog(#"\nTu hai scelto: %#", [simboli objectAtIndex:scelta ]);
switch (casuale) {
case (0):
if (scelta == 0) {
NSLog (#"\n È un pareggio!");
++pareggi;
break;}
else if (scelta == 1) {
NSLog (#"\n Il Computer Vince!");
++computer;
break;}
else if (scelta == 2) {
NSLog (#"\n Hai vinto!");
++giocatore;
break;}
case (1):
if (scelta == 0) {
NSLog (#"\n Hai Vinto!");
++giocatore;
break;}
else if (scelta == 1) {
NSLog (#"\n È un pareggio!");
++pareggi;
break;}
else if (scelta == 2) {
NSLog (#"\n Il computer Vince!");
++computer;
break; }
case (2):
if (scelta == 0) {
NSLog (#"\n Il computer Vince!");
++computer;
break; }
else if (scelta == 1) {
NSLog (#"\n Hai Vinto!");
++giocatore;
break; }
else if (scelta == 2) {
NSLog (#"\n È un pareggio");
++pareggi;
break;}
break;
default:
break;
}
}
- (void)verdetto {
NSLog(#"\n Vittorie giocatore: %ld", giocatore);
NSLog(#"\n Vittorie computer: %ld", computer);
NSLog(#"\n Pareggi: %ld", pareggi);
if (giocatore > computer) NSLog(#"\n Bravo! Hai vinto tu la partita!");
else NSLog(#"\n Hai perso! Il computer ha vinto la partita");
}
#end
Same as you would if they were not the same kind of object, basically. First, make what you want to read public:
// Hand.h
#import <Foundation/Foundation.h>
#interface Hand : NSObject
// Each hand maintains a value
#property (assign) int a;
// This makes it easy to show two Hands working together...not necesary
- (instancetype)initAsFirst:(BOOL)first;
#end
Then, use a reference to one object from inside the other:
// Hand.m
#import "Hand.h"
#implementation Hand
// To start, some other object will call this with first set to YES.
- (instancetype)initAsFirst:(BOOL)first
{
self = [super init];
if (self) {
if (first) {
// The first Hand will create a second one to demonstrate the difference.
self.a = 0;
[self showHands:[[Hand alloc] initAsFirst:NO]];
} else {
// The second Hand will have a different value to prove that it's different.
self.a = 10;
}
}
return self;
}
- (void)showHands:(Hand *)h
{
// Display both the value of this hand (first) and the one this hand created (second)
NSLog(#"Mine: %d, Other: %d", self.a, h.a);
}
#end
now I have 1 class
#interface mani : NSObject
that maybe could control the game result.
and 2 subclasses:
#interface computer : mani //for random number generator
#interface giocatore : mani //for player choosing number
Now the matter for me is letting the first class (mani) read its subclasses variables, and do what to do to determine who is the winner! Still can't get how it works, and belive me, I am reading forums, and e-books!
In my application, I inherited NSWindow class. This is to override default key down event.
//mywindow.h
#interface TWindow: NSWindow
{
}
- (void)keyDown: (NSEvent *)pEvent;
#end
//mywindow.mm
- (void)keyDown:(NSEvent *)theEvent
{
//Detect Control-W and Control-Q and handle them.
unsigned short keycode; ///< Key code.
NSString * charigmtch; ///< Character ignoring matches.
NSString * character;
BOOL cmdkeydown; ///< check if the command key is down.
BOOL shiftkeydown; ///< Check if shift key is down.
//Get the keycode of Control-W
keycode = [theEvent keyCode];
//get character ignoring match.
charigmtch = [theEvent charactersIgnoringModifiers];
//get character
character = [theEvent characters];
cmdkeydown = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask;
shiftkeydown = ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) == NSShiftKeyMask;
//Get the keycode of Control
if(cmdkeydown && 12 == keycode && ![character compare:#"q"] && ![charigmtch compare:#"q"]) {
//CloseWithConfirm shows message box confirming quit.
[self CloseWithConfirm];
} else if (keycode == 48) {
//Tab key is pressed.
//This AppDelegate is application delegate and also NSWindowDelegate.
AppDelegate * delegate; ///< Delegate.
//Get the delegate from the window.
delegate = (AppDelegate *)[self delegate];
//Shift key is not down.
if(!shiftkeydown) {
//Tab key is pressed.
[delegate TabKeyPressed];
} else {
//Shift-Tab key is pressed.
[delegate ShiftTabKeyPressed];
}
}
//Handle the other key.
[super keyDown:theEvent]; //Line E
}
When I run following sample test case:
void TestCase ()
{
MyThread thread1, thread2, thread3, thread4;
//Run thread 1
thread1.Execute();
//Run thread 2
thread2.Execute();
//Run thread 3
thread3.Execute();
//Run thread 4
thread4.Execute();
}
//Following function is executed in thread function.
void Perform ()
{
//This adds the data in table view.
AddRowInTableView ("Test");
}
This code run fines most the time. But sometimes, it crashes at Line E marked in the code.
I am not able to find the reason of this crash. Can anyone help me?
I am using Mac OS X Mavericks preview and Xcode 5 for debugging and building.
Do not update UI in secondary thread. All drawing and UI events must be handled on the main thread.
First of all, I'm an Objective-C novice. So I'm not very familiar with OS X or iOS development. My experience is mostly in Java.
I'm creating an agent-based modeling-framework. I'd like to display the simulations and to do that I'm writing a little application. First, a little bit about the framework. The framework has a World class, in which there is a start method, which iterates over all agents and has them perform their tasks. At the end of one "step" of the world (i.e., after all the agents have done their thing), the start method calls the intercept method of an object that implements InterceptorProtocol. This object was previously passed in via the constructor. Using the interceptor, anyone can get a hook into the state of the world. This is useful for logging, or in the scenario that I'm trying to accomplish: displaying the information in a graphical manner. The call to intercept is synchronous.
Now as far as the GUI app is concerned, it is pretty simple. I have a controller that initializes a custom view. This custom view also implements InterceptorProtocol so that it can listen in, to what happens in the world. I create a World object and pass in the view as an interceptor. The view maintains a reference to the world through a private property and so once I have initialized the world, I set the view's world property to the world I have just created (I realize that this creates a cycle, but I need a reference to the world in the drawRect method of the view and the only way I can have it is if I maintain a reference to it from the class).
Since the world's start method is synchronous, I don't start the world up immediately. In the drawRect method I check to see if the world is running. If it is not, I start it up in a background thread. If it is, I examine the world and display all the graphics that I need to.
In the intercept method (which gets called from start running on the background thread), I set setNeedsToDisplay to YES. Since the start method of the world is running in a separate thread, I also have a lock object that I use to synchronize so that I'm not working on the World object while it's being mutated (this part is kind of janky and it's probably not working the way I expect it to - there are more than a few rough spots and I'm simply trying to get a little bit working; I plan to clean up later).
My problem is that the view renders some stuff, and then it pretty much locks up. I can see that the NSLog statements are being called and so the code is running, but nothing is getting updated on the view.
Here's some of the pertinent code:
MasterViewController
#import "MasterViewController.h"
#import "World.h"
#import "InfectableBug.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_worldView = [[WorldView alloc] init];
World* world = [[World alloc] initWithName: #"Bhumi"
rows: 100
columns: 100
iterations: 2000
snapshotInterval: 1
interceptor: _worldView];
for(int i = 0; i < 999; i++) {
NSMutableString* name = [NSMutableString stringWithString: #"HealthyBug"];
[name appendString: [[NSNumber numberWithInt: i] stringValue]];
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: name
layer: #"FirstLayer"
infected: NO
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
}
NSLog(#"Added all bugs. Going to add infected");
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: #"InfectedBug"
layer: #"FirstLayer"
infected: YES
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
[_worldView setWorld: world];
//[world start];
}
return self;
}
- (NSView*) view {
return self.worldView;
}
#end
WorldView
#import "WorldView.h"
#import "World.h"
#import "InfectableBug.h"
#implementation WorldView
#synthesize world;
- (id) initWithFrame:(NSRect) frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void) drawRect:(NSRect) dirtyRect {
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
CGContextClearRect(myContext, CGRectMake(0, 0, 1024, 768));
NSUInteger rows = [world rows];
NSUInteger columns = [world columns];
NSUInteger cellWidth = 1024 / columns;
NSUInteger cellHeight = 768 / rows;
if([world running]) {
#synchronized (_lock) {
//Ideally we would need layers, but for now let's just get this to display
NSArray* bugs = [world bugs];
NSEnumerator* enumerator = [bugs objectEnumerator];
InfectableBug* bug;
while ((bug = [enumerator nextObject])) {
if([bug infected] == YES) {
CGContextSetRGBFillColor(myContext, 128, 0, 0, 1);
} else {
CGContextSetRGBFillColor(myContext, 0, 0, 128, 1);
}
NSLog(#"Drawing bug %# at %lu, %lu with width %lu and height %lu", [bug name], [bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight);
CGContextFillRect(myContext, CGRectMake([bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight));
}
}
} else {
[world performSelectorInBackground: #selector(start) withObject: nil];
}
}
- (BOOL) isFlipped {
return YES;
}
- (void) intercept: (World *) aWorld {
struct timespec time;
time.tv_sec = 0;
time.tv_nsec = 500000000L;
//nanosleep(&time, NULL);
#synchronized (_lock) {
[self setNeedsDisplay: YES];
}
}
#end
start method in World.m:
- (void) start {
running = YES;
while(currentIteration < iterations) {
#autoreleasepool {
[bugs shuffle];
NSEnumerator* bugEnumerator = [bugs objectEnumerator];
Bug* bug;
while((bug = [bugEnumerator nextObject])) {
NSString* originalLayer = [bug layer];
NSUInteger originalX = [bug x];
NSUInteger originalY = [bug y];
//NSLog(#"Bug %# is going to act and location %i:%i is %#", [bug name], [bug x], [bug y], [self isOccupied: [bug layer] x: [bug x] y: [bug y]] ? #"occupied" : #"not occupied");
[bug act];
//NSLog(#"Bug has acted");
if(![originalLayer isEqualToString: [bug layer]] || originalX != [bug x] || originalY != [bug y]) {
//NSLog(#"Bug has moved");
[self moveBugFrom: originalLayer atX: originalX atY: originalY toLayer: [bug layer] atX: [bug x] atY: [bug y]];
//NSLog(#"Updated bug position");
}
}
if(currentIteration % snapshotInterval == 0) {
[interceptor intercept: self];
}
currentIteration++;
}
}
//NSLog(#"Done.");
}
Please let me know if you'd like to see any other code. I realize that the code is not pretty; I was just trying to get stuff to work and I plan on cleaning it up later. Also, if I'm violating an Objective-C best practices, please let me know!
Stepping out for a bit; sorry if I don't respond immediately!
Whew, quiet a question for probably a simple answer: ;)
UI updates have to be performed on the main thread
If I read your code correctly, you call the start method on a background thread. The start method contains stuff like moveBugFrom:... and also the intercept: method. The intercept method thus calls setNeedsDisplay: on a background thread.
Have all UI related stuff perform on the main thread. Your best bet is to use Grand Central Dispatch, unless you need to support iOS < 4 or OS X < 10.6 (or was it 10.7?), like this:
dispatch_async(dispatch_get_main_queue(), ^{
// perform UI updates
});
I'm a little stuck, i'm trying to write a method which when a button is pressed; will get the current volume of iTunes, store the volume as an int declared as x. Then make the iTunes volume equal to 0, which will essentially mute the iTunes volume, but then i want the iTunes volume to return to the int x if the button is pressed again, which will essentially unmute the iTunes volume and restore it to the original volume.
Here's what i have so far:
- (IBAction)muteAndUnmute:(id)sender {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
int x;
x = [iTunes soundVolume];
if ([iTunes soundVolume] > 0 ) {
[volumeSlider setIntValue:0];
[iTunes setSoundVolume:[volumeSlider intValue]];
[volumeLabel setIntValue:[volumeSlider intValue]];}
}
Any help would be much appreciated, i think it's quite easy to do but just can't get my head around it, thanks in advance, Sami.
make your volume value (vol) as class variable and not local, then
// MyWindowController.h
#interface MyWindowController : NSWindowController {
int vol;
}
- (IBAction)btnPressed:(id)sender;
#end
// MyWindowController.m
#implementation MyWindowController
- (id)init {
if (self = [super init]) {
vol = 0;
}
return self;
}
- (IBAction)btnPressed:(id)sender
{
id iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
if ([iTunes soundVolume] > 0 )
{
vol = [iTunes soundVolume];
[iTunes setSoundVolume:0];
}
else
[iTunes setSoundVolume:vol];
}
#end
you can use this:
fVolume = [MPMusicPlayerController iPodMusicPlayer].volume;
[MPMusicPlayerController iPodMusicPlayer].volume = 0.0;
//do whatever you want
[MPMusicPlayerController iPodMusicPlayer].volume = fVolume;