summaryrefslogtreecommitdiffstats
path: root/generic/tkTextDisp.c
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2019-01-03 21:09:14 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2019-01-03 21:09:14 (GMT)
commit7e88cb3d95e4e109025330d5a72cc2710e9944d9 (patch)
tree97b0fd91356b15b3befd63b36dd3471fb7176f10 /generic/tkTextDisp.c
parent19864dca1d60859c5428554215450a8e7e54f947 (diff)
downloadtk-7e88cb3d95e4e109025330d5a72cc2710e9944d9.zip
tk-7e88cb3d95e4e109025330d5a72cc2710e9944d9.tar.gz
tk-7e88cb3d95e4e109025330d5a72cc2710e9944d9.tar.bz2
Fix bug [b2dd3b4fe8] (text-11a.41 sometimes hangs) by reworking how the
<<WidgetViewSync>> event is handled.
Diffstat (limited to 'generic/tkTextDisp.c')
-rw-r--r--generic/tkTextDisp.c77
1 files changed, 57 insertions, 20 deletions
diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c
index 8d60754..5903dfb 100644
--- a/generic/tkTextDisp.c
+++ b/generic/tkTextDisp.c
@@ -497,13 +497,15 @@ static TkTextDispChunk *baseCharChunkPtr = NULL;
* different character might be under the mouse
* cursor now). Need to recompute the current
* character before the next redisplay.
+ * OUT_OF_SYNC 1 means that the last <<WidgetViewSync>> event had
+ * value 0, indicating that the widget is out of sync.
*/
#define DINFO_OUT_OF_DATE 1
#define REDRAW_PENDING 2
#define REDRAW_BORDERS 4
#define REPICK_NEEDED 8
-
+#define OUT_OF_SYNC 16
/*
* Action values for FreeDLines:
*
@@ -681,7 +683,7 @@ TkTextCreateDInfo(
dInfoPtr->scanTotalYScroll = 0;
dInfoPtr->scanMarkY = 0;
dInfoPtr->dLinesInvalidated = 0;
- dInfoPtr->flags = DINFO_OUT_OF_DATE;
+ dInfoPtr->flags = 0;
dInfoPtr->topPixelOffset = 0;
dInfoPtr->newTopPixelOffset = 0;
dInfoPtr->currentMetricUpdateLine = -1;
@@ -3070,10 +3072,13 @@ AsyncUpdateLineMetrics(
* We have looped over all lines, so we're done. We must release our
* refCount on the widget (the timer token was already set to NULL
* above). If there is a registered aftersync command, run that first.
+ * Cancel any pending idle task which would try to run the command
+ * after the afterSyncCmd pointer had been set to NULL.
*/
if (textPtr->afterSyncCmd) {
int code;
+ Tcl_CancelIdleCall(TkTextRunAfterSyncCmd, textPtr);
Tcl_Preserve((ClientData) textPtr->interp);
code = Tcl_EvalObjEx(textPtr->interp, textPtr->afterSyncCmd,
TCL_EVAL_GLOBAL);
@@ -3091,7 +3096,6 @@ AsyncUpdateLineMetrics(
* with its internal data (actually it will be after the next trip
* through the event loop, because the widget redraws at idle-time).
*/
-
GenerateWidgetViewSyncEvent(textPtr, 1);
if (textPtr->refCount-- <= 1) {
@@ -3115,8 +3119,14 @@ AsyncUpdateLineMetrics(
* GenerateWidgetViewSyncEvent --
*
* Send the <<WidgetViewSync>> event related to the text widget
- * line metrics asynchronous update.
- * This is equivalent to:
+ * line metrics asynchronous update. These events should only
+ * be sent when the sync status has changed. So this function
+ * compares the requested state with the state saved in the
+ * TkText structure, and only generates the event if they are
+ * different. This means that it is safe to call this function
+ * at any time when the state is known.
+ *
+ * If an event is sent, the effect is equivalent to:
* event generate $textWidget <<WidgetViewSync>> -data $s
* where $s is the sync status: true (when the widget view is in
* sync with its internal data) or false (when it is not).
@@ -3132,9 +3142,12 @@ AsyncUpdateLineMetrics(
static void
GenerateWidgetViewSyncEvent(
- TkText *textPtr, /* Information about text widget. */
- Bool InSync) /* true if in sync, false otherwise */
+ TkText *textPtr, /* Information about text widget. */
+ Bool InSync) /* true if becoming in sync, false otherwise */
{
+ Bool NewSyncState = (InSync != 0); /* ensure 0 or 1 value */
+ Bool OldSyncState = !(textPtr->dInfoPtr->flags & OUT_OF_SYNC);
+
/*
* OSX 10.14 needs to be told to display the window when the Text Widget
* is in sync. (That is, to run DisplayText inside of the drawRect
@@ -3147,8 +3160,15 @@ GenerateWidgetViewSyncEvent(
FORCE_DISPLAY(textPtr->tkwin);
}
- TkSendVirtualEvent(textPtr->tkwin, "WidgetViewSync",
- Tcl_NewBooleanObj(InSync));
+ if (NewSyncState != OldSyncState) {
+ if (NewSyncState) {
+ textPtr->dInfoPtr->flags &= ~OUT_OF_SYNC;
+ } else {
+ textPtr->dInfoPtr->flags |= OUT_OF_SYNC;
+ }
+ TkSendVirtualEvent(textPtr->tkwin, "WidgetViewSync",
+ Tcl_NewBooleanObj(NewSyncState));
+ }
}
/*
@@ -3191,6 +3211,9 @@ TkTextUpdateLineMetrics(
TkTextLine *linePtr = NULL;
int count = 0;
int totalLines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
+ int fullUpdateRequested = (lineNum == 0 &&
+ endLine == totalLines &&
+ doThisMuch == -1);
if (totalLines == 0) {
/*
@@ -3201,6 +3224,7 @@ TkTextUpdateLineMetrics(
}
while (1) {
+
/*
* Get a suitable line.
*/
@@ -3227,6 +3251,7 @@ TkTextUpdateLineMetrics(
*/
if (textPtr->dInfoPtr->metricEpoch == -1 && lineNum == endLine) {
+
/*
* We have looped over all lines, so we're done.
*/
@@ -3250,10 +3275,12 @@ TkTextUpdateLineMetrics(
if (TkBTreeLinePixelEpoch(textPtr, linePtr)
== textPtr->dInfoPtr->lineMetricUpdateEpoch) {
+
/*
* This line is already up to date. That means there's nothing
* to do here.
*/
+
} else if (doThisMuch == -1) {
count += 8 * TkTextUpdateOneLine(textPtr, linePtr, 0,NULL,0);
} else {
@@ -3275,6 +3302,7 @@ TkTextUpdateLineMetrics(
indexPtr = &textPtr->dInfoPtr->metricIndex;
pixelHeight = textPtr->dInfoPtr->metricPixelHeight;
} else {
+
/*
* We must reset the partial line height calculation data
* here, so we don't use it when it is out of date.
@@ -3298,6 +3326,7 @@ TkTextUpdateLineMetrics(
pixelHeight, indexPtr, 1);
if (indexPtr->linePtr == linePtr) {
+
/*
* We didn't complete the logical line, because it
* produced very many display lines, which must be because
@@ -3306,6 +3335,7 @@ TkTextUpdateLineMetrics(
*/
if (pixelHeight == 0) {
+
/*
* These have already been stored, unless we just
* started the new line.
@@ -3327,6 +3357,7 @@ TkTextUpdateLineMetrics(
textPtr->dInfoPtr->metricEpoch = -1;
}
} else {
+
/*
* We must never recalculate the height of the last artificial
* line. It must stay at zero, and if we recalculate it, it will
@@ -3351,13 +3382,21 @@ TkTextUpdateLineMetrics(
}
}
if (doThisMuch == -1) {
+
/*
- * If we were requested to provide a full update, then also update the
- * scrollbar.
+ * If we were requested to update the entire range, then also update
+ * the scrollbar.
*/
GetYView(textPtr->interp, textPtr, 1);
}
+ if (fullUpdateRequested) {
+ TextDInfo *dInfoPtr = textPtr->dInfoPtr;
+
+ dInfoPtr->lastMetricUpdateLine = lineNum;
+ dInfoPtr->currentMetricUpdateLine = lineNum;
+ GenerateWidgetViewSyncEvent(textPtr, 1);
+ }
return lineNum;
}
@@ -3526,8 +3565,12 @@ TextInvalidateLineMetrics(
textPtr->refCount++;
dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
AsyncUpdateLineMetrics, textPtr);
- GenerateWidgetViewSyncEvent(textPtr, 0);
}
+
+ /*
+ * The widget is out of sync: send a <<WidgetViewSync>> event.
+ */
+ GenerateWidgetViewSyncEvent(textPtr, 0);
}
/*
@@ -5269,9 +5312,7 @@ TkTextRelayoutWindow(
inSync = 0;
}
- if (!inSync) {
- GenerateWidgetViewSyncEvent(textPtr, 0);
- }
+ GenerateWidgetViewSyncEvent(textPtr, inSync);
}
}
@@ -6296,11 +6337,7 @@ TkTextPendingsync(
{
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- return (
- (!(dInfoPtr->flags & REDRAW_PENDING) &&
- (dInfoPtr->metricEpoch == -1) &&
- (dInfoPtr->lastMetricUpdateLine == dInfoPtr->currentMetricUpdateLine)) ?
- 0 : 1);
+ return ((dInfoPtr->flags & OUT_OF_SYNC) != 0);
}
/*