AppleScript ObjC App skips on mouse movement

Hello friends!

Long time reader, first time poster. This forum has been an excellent resource for me as I’ve learned to build programs with Xcode for the company I work for. I have finally hit a bug that I cannot find an answer for anywhere though.

Happy to post code snippets if needed, but I don’t think this is script related. Basically the program is an ffmpeg/rsync utility that automatically takes videos people record out in the field, compresses them, and then uploads them to our servers here at work. I use expect scripts to handle output from the rsync command. For progress reporting I run the scripts as background processes with stdout and stderr going to a .txt file that is read and parsed in loops that repeat until the output at the end of the loop equals the output at the beginning. All commands are referenced inside the Resources of the application.

So here is what keeps happening. When I clean, build, and run the program everything works perfectly, as long as I don’t touch the mouse. As soon as I touch the mouse or click it the program will skip forward in the script. It happens across the entire app. It is mostly noticed in my progress repeat loops that will exit moving on to the next process that it is not ready for. The bug continues to occur after the app has been archived and on all machines it is run on. I have already tried rebuilding from scratch, though a lot of things were copied from what I had deemed the “unstable” version. What on earth could be causing this strange behavior!!!

I have tried everything I can think of. Please help!!!

To add,

this is cocoa applescript (I assume the same thing but I just wanted to verify)

also, i am currently researching if there is a way to have the app ignore mouse events during the repeat loops. kind of a work around. I am having no luck as of yet so if anyone knows of a way to do this that’d be awesome. Unless of course you can figure out the underlying problem

Do you have any handlers capturing mouse events?

None that I am aware of, except for buttons that act as senders to initiate parts of the script. My expertise in these cocoa-applescript apps is really just the applescript and the bourne commands within. I will be buying your book soon to hopefully learn more about the ObjC side of these things but for now those files remain untouched in my app.

I apologize in advance for not having many resources to dedicate to the troubleshooting. The program doesn’t go anywhere if you don’t have a username and password to ssh into our servers. I could share the project (as I feel this is a problem with it as a whole and not necessarily a single line somewhere), but the issue could really only be reproduced if I set up credentials for someone on my end. I am very trusting of these types of communities, but my bosses are not so much and if something bad were to happen…well I’d have some explaining to do haha. I suppose it could be easily adapted for any other ssh server though if someone is really dedicated to helping me figure this one out :slight_smile:

EDIT: I am noticing some other weird problems now in Xcode that are happening across other projects. Nothing affecting functionality, but for instance, when a window is selected in the interface builder, the screen position tab of the utility pane is blank across all of my projects out of nowhere. Perhaps I should try removing and reinstalling Xcode. If I were to do so would this affect any of my projects?

EDIT 2: I’m an idiot. the screen positioning/sizing pane was collapsed inside itself. I didn’t even know you could do that (and frankly I don’t see why you can lol) Still having the original issue though

Perhaps if you were to post the main code of the repeat loop you talk of, we might get a better idea of what’s happening.

There are a few of them, but here is the repeat loop that handles compression with it’s context. apologies for any redundancy or anything that makes you just think “why the heck did he do it that way”. This thing has been kind of an ongoing project since the onset of me learning Xcode and my thoughts were very disconnected at times. The file drop area is basically a huge text field as for the life of me I cannot figure out drop areas in Xcode for files. I like to think I am smarter then this, but I clearly need to learn more about how the underlying ObjC works.

    on initiateCompression_(sender)
        -- First we want to make sure we have all the information we need, and remove any temp files that might be left from a previous session
            do shell script "rm -rf /tmp/*"
        end try
        -- find out have many files have been specified and build a string accordingly
        -- The initial string will first be modified to get rid of any empty entries, and then will be run through a subroutine that compensates for any characters that need to be escaped to get past the UNIX shell
        set single_string to (stringValue() of files_drop) as string
        set AppleScript's text item delimiters to {return, linefeed, return & linefeed, linefeed & return}
        set file_list to text items of single_string as list
        set AppleScript's text item delimiters to {}
        set files_to_compress to {}
        set files_to_upload to {}
        repeat with i from 1 to number of items in file_list
            set this_item to item i of file_list
            set this_item to replaceString(this_item, " ", "\\ ")
            set this_item to replaceString(this_item, "(", "\\(")
            set this_item to replaceString(this_item, ")", "\\)")
            set files_to_compress's end to this_item
        end repeat
        if files_to_compress is {}
            error_dialog's setStringValue_("No files specified")
            error_window's orderFront_(me)
            error number 128
        end if
        -- if compression is enabled run the files through the bundled ffmpeg application
        set compression to (stringValue() of compression_enabled) as text
        if compression is equal to "1" then
        -- set the location of ffmpeg to a variable
                set path_to_resources to (POSIX path of (path to current application))
                set ffmpeg to (path_to_resources & "Contents/Resources/ffmpeg") as Unicode text
            on error errMsg
                error_dialog's setStringValue_("Could not locate the compressor. Try without compression or contact IT: " & errMsg)
                error_window's orderFront_(me)
                error number -128
            end try
            set total_time to 0
            tell progress_bar to startAnimation:me
            progress_label's setStringValue_("Preparing files for compression")
            repeat with a from 1 to number of items in files_to_compress
                set this_file to item a of files_to_compress as text
                set getinfo to (ffmpeg & " -i " & this_file & " &> /tmp/output.txt &")
                do shell script getinfo
                delay 0.5
                set the_specs to (do shell script "grep \"Duration\" /tmp/output.txt")
                set full_duration to characters 13 thru 23 of the_specs as text
                set duration to convertduration(full_duration)
                set total_time to total_time + duration
                do shell script "rm -rf /tmp/output.txt"
            end repeat
        -- run the files through the compressor updating the progress indicators as necessary.
                tell progress_bar to setIndeterminate:FALSE
                tell progress_bar to setMaxValue:total_time
                tell progress_bar to setDoubleValue:0
                progress_label's setStringValue_("Compressing the media...")
                repeat with a from 1 to number of items in files_to_compress
                    set this_file to item a of files_to_compress
                    set Applescript's text item delimiters to "."
                    set file_path to text item 1 of this_file
                    set extension to last text item of this_file
                    if (extension is not equal to "mov") then
                        display dialog "Compression for non-mov files is not supported!"
                        error number -128
                    end if
                    set AppleScript's text item delimiters to "/"
                    set file_name to text item -1 of file_path
                    set file_name to replaceString(file_name, " ", "")
                    delay 0.5
                    set compress_script to ffmpeg & " -i " & this_file & " -map 0:0 -map 0:1 -map 0:2 -map 0:3 -map 0:4 -map 0:5 -map 0:6 -map 0:7 -map 0:8 -acodec copy -codec:v mpeg4 -b:v 8000k -threads 0 -codec:a libvo_aacenc /tmp/" & file_name & ".mp4 &> /tmp/output.txt &"
                    do shell script compress_script
                    delay 3
                    my compressionProgress()
                end repeat
                tell progress_bar to setIndeterminate:true
                do shell script "rm -rf /tmp/output.txt"
            on error
                error_dialog's setStringValue_("Compression failed! Try without compression or contact IT")
                error_window's orderFront_(me)
                error number -128
            end try
            set files_to_upload to (paragraphs of (do shell script "ls /tmp/*.mp4")) as list
            tell progress_bar to setIndeterminate:TRUE
            delay 0.5
            tell progress_bar to startAnimation:me
            progress_label's setStringValue_("Preparing files for upload...")
            delay 0.5
            repeat with a from 1 to number of items in files_to_compress
                set this_file to item a of files_to_compress
                set files_to_upload's end to this_file
                set copy_script to "cp -r" & space & this_file & space & "/tmp/"
                do shell script copy_script
            end repeat
            delay 0.5
        end if
        my initiateUpload_()

end initiateCompression_

Here are the relevant subroutines to that section.

    on compressionProgress()
        set initial_duration to 0
            delay 0.5
            set the_output to paragraph -3 of (do shell script "cat /tmp/output.txt|tail")
            set full_duration to characters 48 thru 59 of the_output as text
            set full_duration to replaceString(full_duration, " ", "")
            set full_duration to replaceString(full_duration, "/", "")
            set current_duration to convertduration(full_duration)
            set the_difference to (current_duration - initial_duration)
            set initial_duration to current_duration
            tell progress_bar to incrementBy:the_difference
            if the_difference is 0 then
                do shell script "rm -rf /tmp/output.txt"
                delay 0.5
                exit repeat
            end if
            delay 0.5
        end repeat
    end compressionProgress

    on convertduration(full_duration)
        set h to characters 1 thru 2 of full_duration as text
        set m to characters 4 thru 5 of full_duration as text
        set s to characters 7 thru 8 of full_duration as text
        set ms to characters 10 thru 11 of full_duration as text
        set ms_hour to h * 36000
        set ms_min to m * 6000
        set ms_sec to s * 100
        set new_duration to (ms_hour + ms_min + ms_sec + ms)
        return new_duration
    end convertduration

    on replaceString(theText, oldString, newString)
        set AppleScript's text item delimiters to oldString
        set tempList to every text item of theText
        set AppleScript's text item delimiters to newString
        set theText to the tempList as string
        set AppleScript's text item delimiters to ""
        return theText
    end replaceString

I am aware that I am starting to push Applescript’s abilities to their limits. That’s why it’s time to start running with this newfound passion of mine :slight_smile:

So what you really need to do is get rid of most, if not all, uses of the delay command, and use NSTimer instead. Instead of sitting in a loop, your bits of code will be in handlers called periodically by timers.

You will probably also need to use displayIfNeeded() to update the display periodically. This is something the delay command sort of handles as well, but the way it does it is partly the cause of your problems. And I suspect it’s in your script several times more for the side-effect of updating things than any need to bring the process to a halt.

Good luck.

Thanks so much! Just for some clarification. I have never used displayifneeded before. Would the syntax for that in my case be as simple as something like this instead of my current setStringValue()'s

tell progress_label to displayIfNeeded("Current progress here")

Sorry i needed to edit hopefully I didn’t throw you off Shane. as I think I am sort of understanding it.

on myFunction_(sender)
do shell script (start_my_compression_here)
set myTimer to NSTimer’s scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1, me, “compressionProgress:”,
end myFunction_

on compressionProgress_(sender)
–repeat loop for displaying progress in here (minus the repeat part)
end compressionProgress_

No. You usually have an outlet to the window, say theWindow, and use:

theWindow's displayIfNeeded()

whenever you want to update the display within long-running code.

You’ll probably want to use scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:, which creates it and queues it at the same time.

The above is where you do the work you’re now doing in your repeat loop.

No, your timer does it for you. If you really need just a delay, you can possibly use performSelector:withObject:afterDelay:, or NSThread’s sleepForTimeInterval:.

I don’t want to sound like an ad, but this stuff is hard to in a few posts here – that’s why I wrote a book on it.

You’re absolutely right and I appreciate you taking the time to help. Since I am doing this all for my company I was kind of holding out to see if I can’t talk them into buying it for me. I’ll play around with the information you’ve provided me and check back if I run into any trouble or if all gets fixed.

Thanks again!

Good News!

NSThread’s sleepForTimeInterval: in place of my delays solved all my issues for now. I can see where I need to add displayIfNeeded and I will be sure to take care of that, but I am about to retire for the weekend.

After reading the documentation on NSTimer you are absolutely right that that is how I should be handling this, but I will wait until I get my hands on your book to get a better grasp on how to use the ObjC framework within my cocoa apps.