1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <rtl/math.hxx>
23 #include <tools/time.hxx>
25 #include <osx/saltimer.h>
26 #include <osx/salnstimer.h>
27 #include <osx/saldata.hxx>
28 #include <osx/salframe.h>
29 #include <osx/salinst.h>
32 void ImplNSAppPostEvent( short nEventId
, BOOL bAtStart
, int nUserData
)
34 ReleasePoolHolder aPool
;
35 SAL_WNODEPRECATED_DECLARATIONS_PUSH
36 // 'NSApplicationDefined' is deprecated: first deprecated in macOS 10.12
37 NSEvent
* pEvent
= [NSEvent otherEventWithType
: NSApplicationDefined
38 SAL_WNODEPRECATED_DECLARATIONS_POP
41 timestamp
: [[NSProcessInfo processInfo
] systemUptime
]
52 // nextEventMatchingMask has to run in the main thread!
53 assert([NSThread isMainThread
]);
55 // Posting an event to the end of an empty queue fails,
56 // so we peek the queue and post to the start, if empty.
57 // Some Qt bugs even indicate nextEvent without dequeue
58 // sometimes blocks, so we dequeue and re-add the event.
59 NSEvent
* pPeekEvent
= [NSApp nextEventMatchingMask
: NSEventMaskAny
61 inMode
: NSDefaultRunLoopMode
63 if ( nil
== pPeekEvent
)
66 [NSApp postEvent
: pPeekEvent atStart
: YES
];
68 [NSApp postEvent
: pEvent atStart
: bAtStart
];
71 void AquaSalTimer::queueDispatchTimerEvent( bool bAtStart
)
74 m_bDirectTimeout
= true;
75 ImplNSAppPostEvent( AquaSalInstance::DispatchTimerEvent
,
76 bAtStart
, GetNextEventVersion() );
79 void AquaSalTimer::Start( sal_uInt64 nMS
)
81 SalData
* pSalData
= GetSalData();
83 if( !pSalData
->mpInstance
->IsMainThread() )
85 ImplNSAppPostEvent( AquaSalInstance::AppStartTimerEvent
, YES
, nMS
);
89 m_bDirectTimeout
= (0 == nMS
) && !pSalData
->mpInstance
->mbIsLiveResize
;
90 if ( m_bDirectTimeout
)
94 NSTimeInterval aTI
= double(nMS
) / 1000.0;
95 if( m_pRunningTimer
!= nil
)
97 if ([m_pRunningTimer isValid
] && rtl::math::approxEqual(
98 [m_pRunningTimer timeInterval
], aTI
))
101 [m_pRunningTimer setFireDate
: [NSDate dateWithTimeIntervalSinceNow
: aTI
]];
108 if( m_pRunningTimer
== nil
)
110 m_pRunningTimer
= [[NSTimer scheduledTimerWithTimeInterval
: aTI
111 target
: [[[TimerCallbackCaller alloc
] init
] autorelease
]
112 selector
: @
selector(timerElapsed
:)
116 /* #i84055# add timer to tracking run loop mode,
117 so they also elapse while e.g. life resize
119 [[NSRunLoop currentRunLoop
] addTimer
: m_pRunningTimer forMode
: NSEventTrackingRunLoopMode
];
124 void AquaSalTimer::Stop()
126 assert( GetSalData()->mpInstance
->IsMainThread() );
128 if( m_pRunningTimer
!= nil
)
130 [m_pRunningTimer invalidate
];
131 [m_pRunningTimer release
];
132 m_pRunningTimer
= nil
;
137 void AquaSalTimer::callTimerCallback()
139 ImplSVData
* pSVData
= ImplGetSVData();
140 SolarMutexGuard aGuard
;
141 m_bDirectTimeout
= false;
142 if( pSVData
->maSchedCtx
.mpSalTimer
)
143 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
146 void AquaSalTimer::handleTimerElapsed()
148 if ( m_bDirectTimeout
|| GetSalData()->mpInstance
->mbIsLiveResize
)
150 // Stop the timer, as it is just invalidated after the firing function
155 queueDispatchTimerEvent( YES
);
158 bool AquaSalTimer::handleDispatchTimerEvent( NSEvent
*pEvent
)
160 bool bIsValidEvent
= IsValidEventVersion( [pEvent data1
] );
163 return bIsValidEvent
;
166 void AquaSalTimer::handleStartTimerEvent( NSEvent
* pEvent
)
168 ImplSVData
* pSVData
= ImplGetSVData();
169 if( pSVData
->maSchedCtx
.mpSalTimer
)
171 NSTimeInterval posted
= [pEvent timestamp
] + NSTimeInterval([pEvent data1
])/1000.0;
172 NSTimeInterval current
= [NSDate timeIntervalSinceReferenceDate
];
173 sal_uLong nTimeoutMS
= 0;
174 if( (posted
- current
) > 0.0 )
175 nTimeoutMS
= ceil( (posted
- current
) * 1000 );
180 bool AquaSalTimer::IsTimerElapsed() const
182 assert( !((ExistsValidEvent() || m_bDirectTimeout
) && m_pRunningTimer
) );
183 if ( ExistsValidEvent() || m_bDirectTimeout
)
185 if ( !m_pRunningTimer
)
187 NSDate
* pDt
= [m_pRunningTimer fireDate
];
188 return pDt
&& ([pDt timeIntervalSinceNow
] < 0);
191 AquaSalTimer::AquaSalTimer( )
192 : m_pRunningTimer( nil
)
196 AquaSalTimer::~AquaSalTimer()
201 void AquaSalTimer::handleWindowShouldClose()
203 // for whatever reason events get filtered on close, presumably by
204 // timestamp so post a new timeout event, if there was one queued...
205 if ( ExistsValidEvent() && !m_pRunningTimer
)
206 queueDispatchTimerEvent( NO
);
209 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */