/* dvdisaster: Additional error correction for optical media. * Copyright (C) 2004-2017 Carsten Gnoerlich. * Copyright (C) 2019-2021 The dvdisaster development team. * * Email: support@dvdisaster.org * * This file is part of dvdisaster. * * dvdisaster is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dvdisaster is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with dvdisaster. If not, see . */ /*** src type: only GUI code ***/ #ifdef WITH_GUI_YES #include "dvdisaster.h" #include "read-linear.h" #include "scsi-layer.h" #define C2_CLAMP_VALUE 2352 /*** *** Forward declarations ***/ static void redraw_curve(cairo_t *cr); static void update_geometry(void); /*** *** Routines for updating the GUI from the action thread. ***/ /* * Set the (predicted) maximum reading speed */ static gboolean max_speed_idle_func(gpointer data) { gtk_widget_queue_draw(Closure->readLinearCurveArea); return FALSE; } void GuiInitializeCurve(void *rc_ptr, int max_rate, int can_c2) { read_closure *rc = (read_closure*)rc_ptr; int i; if(!Closure->guiMode) return; Closure->readLinearCurve->maxY = max_rate; Closure->readLinearCurve->maxX = rc->image->dh->sectors/512; Closure->readLinearCurve->logMaxY = C2_CLAMP_VALUE; if(can_c2) Closure->readLinearCurve->enable = DRAW_FCURVE | DRAW_LCURVE; else Closure->readLinearCurve->enable = DRAW_FCURVE; rc->lastCopied = (1000*rc->firstSector)/rc->image->dh->sectors; rc->lastPlotted = rc->lastSegment = rc->lastCopied; if(Closure->readLinearSpiral) for(i=rc->lastCopied-1; i>=0; i--) { Closure->readLinearSpiral->segmentColor[i] = Closure->blueSector; Closure->readLinearCurve->ivalue[i] = 0; } g_idle_add(max_speed_idle_func, NULL); } /* * Drawing the reading speed curve */ typedef struct { read_closure *rc; int percent; } curve_info; static gboolean curve_idle_func(gpointer data) { curve_info *ci = (curve_info*)data; read_closure *rc=ci->rc; char *utf,buf[80]; gint i; /*** Update the textual output */ g_snprintf(buf, 80, _("Current Speed: %d.%dx"), (int)Closure->readLinearCurve->fvalue[ci->percent], (int)(fmod(10*Closure->readLinearCurve->fvalue[ci->percent],10))); utf = g_locale_to_utf8(buf, -1, NULL, NULL, NULL); gtk_label_set_text(GTK_LABEL(Closure->readLinearSpeed), utf); g_free(utf); g_snprintf(buf, 80, _("Unreadable / skipped sectors: %" PRId64), Closure->readErrors); utf = g_locale_to_utf8(buf, -1, NULL, NULL, NULL); gtk_label_set_text(GTK_LABEL(Closure->readLinearErrors), utf); g_free(utf); /*** Update color of the changed spiral segments */ for(i=rc->lastSegment; ipercent; i++) switch(Closure->readLinearCurve->ivalue[i]) { case 0: GuiSetSpiralSegmentColor(Closure->readLinearSpiral, Closure->blueSector, 0, i); break; case 1: GuiSetSpiralSegmentColor(Closure->readLinearSpiral, Closure->greenSector, 0, i); break; case 2: GuiSetSpiralSegmentColor(Closure->readLinearSpiral, Closure->redSector, 0, i); break; case 3: GuiSetSpiralSegmentColor(Closure->readLinearSpiral, Closure->darkSector, 0, i); break; case 4: GuiSetSpiralSegmentColor(Closure->readLinearSpiral, Closure->yellowSector, 0, i); break; } rc->lastSegment = ci->percent; /* Don't touch the curve if 2nd or higher reading pass, of if there is no new data */ if(rc->pass || rc->lastPlotted >= ci->percent) { g_free(ci); g_mutex_lock(rc->rendererMutex); rc->activeRenderers--; g_mutex_unlock(rc->rendererMutex); return FALSE; } /*** Resize the Y axes if speed value exceeds current maximum */ for(i=rc->lastPlotted+1; i<=ci->percent; i++) if(Closure->readLinearCurve->fvalue[i] > Closure->readLinearCurve->maxY) Closure->readLinearCurve->maxY = Closure->readLinearCurve->fvalue[i]; /*** Schedule the curve for redrawing */ rc->lastPlotted = ci->percent; gtk_widget_queue_draw(Closure->readLinearCurveArea); g_free(ci); g_mutex_lock(rc->rendererMutex); rc->activeRenderers--; g_mutex_unlock(rc->rendererMutex); return FALSE; } /* * Add one new data point */ void GuiAddCurveValues(void *rc_ptr, int percent, int color, int c2) { read_closure *rc = (read_closure*)rc_ptr; curve_info *ci; int i; if(!Closure->guiMode || percent < 0 || percent > 1000) return; ci = g_malloc(sizeof(curve_info)); ci->rc = rc; ci->percent = percent; /*** Mark unused speed values between lastCopied and Percent */ if(!rc->pass) { int c2_clamped = c2 < C2_CLAMP_VALUE ? c2 : C2_CLAMP_VALUE; Closure->readLinearCurve->fvalue[percent] = rc->speed; Closure->readLinearCurve->lvalue[percent] = c2_clamped; for(i=rc->lastCopied+1; ireadLinearCurve->fvalue[i] = rc->speed > 0.0 ? -1.0 : 0.0; Closure->readLinearCurve->lvalue[i] = c2_clamped; } } /*** Mark the spiral segments between lastCopied and Percent*/ /* lastCopied+1 ? */ if(rc->lastCopied <= percent) { for(i=rc->lastCopied; i<=percent; i++) Closure->readLinearCurve->ivalue[i] = color; rc->lastCopied = percent; } g_mutex_lock(rc->rendererMutex); rc->activeRenderers++; g_mutex_unlock(rc->rendererMutex); g_idle_add(curve_idle_func, ci); } /* * Mark existing sectors with the dark green color. */ static gboolean curve_mark_idle_func(gpointer data) { gtk_widget_queue_draw(Closure->readLinearSpiral->widget); return FALSE; } void GuiMarkExistingSectors(void) { int i; int x; if(!Closure->guiMode) return; x = Closure->readLinearCurve->rightX + 20; Closure->additionalSpiralColor = 3; for(i=0; i<1000; i++) if(Closure->readLinearSpiral->segmentColor[i] == Closure->greenSector) { Closure->readLinearSpiral->segmentColor[i] = Closure->darkSector; Closure->readLinearCurve->ivalue[i] = 3; } g_idle_add(curve_mark_idle_func, NULL); } /* * Redraw the whole curve */ static void redraw_curve(cairo_t *cr) { GuiRedrawAxes(cr, Closure->readLinearCurve); GuiRedrawCurve(cr, Closure->readLinearCurve, 1000); } /* Calculate the geometry of the curve */ static void update_geometry(void) { GuiUpdateCurveGeometry(Closure->readLinearCurve, "99x", 10); /* Label positions in the foot line */ gtk_box_set_child_packing(GTK_BOX(Closure->readLinearFootlineBox), Closure->readLinearSpeed, TRUE, TRUE, Closure->readLinearCurve->leftX, GTK_PACK_START); gtk_box_set_child_packing(GTK_BOX(Closure->readLinearFootlineBox), Closure->readLinearErrors, TRUE, TRUE, Closure->readLinearCurve->leftX, GTK_PACK_START); } static void redraw_spiral_labels(cairo_t *cr) { int x,w,h; int pos = 1; /* Draw and label the spiral */ x = 10; GuiSetText(Closure->readLinearCurve->layout, _("Medium state"), &w, &h); gdk_cairo_set_source_rgba(cr, Closure->curveColor); cairo_move_to(cr, x, Closure->readLinearCurve->topY - h - 5); pango_cairo_show_layout(cr, Closure->readLinearCurve->layout); if(Closure->additionalSpiralColor == 0) GuiDrawSpiralLabel(cr, Closure->readLinearSpiral, Closure->readLinearCurve->layout, _("Not touched this time"), Closure->curveColor, x, -1); if(Closure->additionalSpiralColor == 3) GuiDrawSpiralLabel(cr, Closure->readLinearSpiral, Closure->readLinearCurve->layout, _("Already present"), Closure->darkSector, x, -1); GuiDrawSpiralLabel(cr, Closure->readLinearSpiral, Closure->readLinearCurve->layout, _("Successfully read"), Closure->greenSector, x, pos++); if(Closure->crcBuf && Closure->crcBuf->crcCached) GuiDrawSpiralLabel(cr, Closure->readLinearSpiral, Closure->readLinearCurve->layout, _("Sectors with CRC errors"), Closure->yellowSector, x, pos++); GuiDrawSpiralLabel(cr, Closure->readLinearSpiral, Closure->readLinearCurve->layout, _("Unreadable / skipped"), Closure->redSector, x, pos++); GuiDrawSpiral(cr, Closure->readLinearSpiral); } static gboolean draw_curve_cb(GtkWidget *widget, cairo_t *cr, gpointer data) { update_geometry(); redraw_curve(cr); return TRUE; } static gboolean draw_spiral_cb(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkAllocation a = {0}; gtk_widget_get_allocation(widget, &a); GuiSetSpiralWidget(Closure->readLinearSpiral, widget); /* Override spiral center */ Closure->readLinearSpiral->mx = a.width - 15 - Closure->readLinearSpiral->diameter / 2; if(Closure->crcBuf && Closure->crcBuf->crcCached) { int w,h; GuiSetText(Closure->readLinearCurve->layout, _("Sectors with CRC errors"), &w, &h); Closure->readLinearSpiral->my = a.height/2 - h; } redraw_spiral_labels(cr); return TRUE; } /*** *** Reset the notebook contents for new scan/read action ***/ void GuiResetLinearReadWindow() { gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->readLinearNotebook), 0); GuiZeroCurve(Closure->readLinearCurve); GuiFillSpiral(Closure->readLinearSpiral, &transparent); if (Closure->readLinearSpiral->widget) gtk_widget_queue_draw(Closure->readLinearSpiral->widget); } /* * Re-layout and redraw the curve drawing area while it is in use. * Required to add the information that CRC data is available, * since this happens when the the initial rendering of the window * contents have already been carried out. */ static gboolean redraw_idle_func(gpointer data) { gtk_widget_queue_draw(Closure->readLinearCurveArea); return FALSE; } void GuiRedrawReadLinearWindow(void) { if(Closure->guiMode) g_idle_add(redraw_idle_func, NULL); } /*** *** Create the notebook contents for the reading and scanning action ***/ void GuiCreateLinearReadWindow(GtkWidget *parent) { GtkWidget *sep,*ignore,*curve,*spiral,*notebook,*hbox; Closure->readLinearHeadline = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(Closure->readLinearHeadline), 0.0); gtk_widget_set_margin_start(Closure->readLinearHeadline, 5); gtk_label_set_ellipsize(GTK_LABEL(Closure->readLinearHeadline), PANGO_ELLIPSIZE_END); gtk_box_pack_start(GTK_BOX(parent), Closure->readLinearHeadline, FALSE, FALSE, 3); sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0); sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(parent), hbox, TRUE, TRUE, 0); curve = Closure->readLinearCurveArea = gtk_drawing_area_new(); gtk_box_pack_start(GTK_BOX(hbox), curve, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(curve), "draw", G_CALLBACK(draw_curve_cb), NULL); Closure->readLinearSpiral = GuiCreateSpiral(&transparent, 10, 5, 1000); spiral = gtk_drawing_area_new(); gtk_widget_set_size_request(spiral, Closure->readLinearSpiral->diameter + 20, -1); gtk_box_pack_start(GTK_BOX(hbox), spiral, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(spiral), "draw", G_CALLBACK(draw_spiral_cb), NULL); notebook = Closure->readLinearNotebook = gtk_notebook_new(); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); gtk_box_pack_end(GTK_BOX(parent), notebook, FALSE, FALSE, 0); hbox = Closure->readLinearFootlineBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); Closure->readLinearSpeed = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(Closure->readLinearSpeed), 0.0); gtk_box_pack_start(GTK_BOX(hbox), Closure->readLinearSpeed, FALSE, FALSE, 0); Closure->readLinearErrors = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(Closure->readLinearErrors), 1.0); gtk_box_pack_start(GTK_BOX(hbox), Closure->readLinearErrors, TRUE, TRUE, 0); ignore = gtk_label_new("progress_tab"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, ignore); Closure->readLinearFootline = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(Closure->readLinearFootline), 0.0); gtk_widget_set_margin_start(Closure->readLinearFootline, 5); ignore = gtk_label_new("footer_tab"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), Closure->readLinearFootline, ignore); Closure->readLinearCurve = GuiCreateCurve(curve, _("Speed"), "%dx", 1000, CURVE_MEGABYTES); Closure->readLinearCurve->leftLogLabel = g_strdup(_("C2 errors")); } #endif /* WITH_GUI_YES */